.net6在preview4时给我们带来了一个新的API:WebApplication,通过这个API我们可以打造更小的轻量级API服务。今天我们来尝试一下如何使用WebApplication设计一个小型API服务系统。

  环境准备

  .NETSDK   v6.0.0-preview.6.21355.2

  Visual Studio 2022 Preview

  首先看看原始版本的WebApplication,官方已经提供了样例模板,打开我们的vs2022,选择新建项目选择asp.net core empty,framework选择.net6.0(preview)点击创建,即可生成一个简单的最小代码示例:

  如果我们在.csproj里在配置节PropertyGroup增加使用C#10新语法让自动进行类型推断来隐式的转换成委托,则可以更加精简:

  <PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>

  当然仅仅是这样,是无法用于生产的,毕竟不可能所有的业务单元我们塞进这么一个小小的表达式里。不过借助WebApplication我们可以打造一个轻量级的系统,可以满足基本的依赖注入的小型服务。比如通过自定义特性类型,在启动阶段告知系统为哪些服务注入哪些访问路径,形成路由键和终结点。具体代码如下:

  首先我们创建一个简易的特性类,只包含httpmethod和path:

    [AttributeUsage(AttributeTargets.Method)]
public class WebRouter : Attribute
{
public string path;
public HttpMethod httpMethod;
public WebRouter(string path)
{
this.path = path;
this.httpMethod = HttpMethod.Post;
}
public WebRouter(string path, HttpMethod httpMethod)
{
this.path = path;
this.httpMethod = httpMethod;
}
}

  接着我们按照一般的分层设计一套DEMO应用层/仓储服务:

    public interface IMyService
{
Task<MyOutput> Hello(MyInput input);
}
public interface IMyRepository
{
Task<bool> SaveData(MyOutput data);
}
public class MyService : IMyService
{
private readonly IMyRepository myRepository;
public MyService(IMyRepository myRepository)
{
this.myRepository = myRepository;
}
[WebRouter("/", HttpMethod.Post)]
public async Task<MyOutput> Hello(MyInput input)
{
var result = new MyOutput() { Words = $"hello {input.Name ?? "nobody"}" };
await myRepository.SaveData(result);
return await Task.FromResult(result);
}
}
public class MyRepository : IMyRepository
{
public async Task<bool> SaveData(MyOutput data)
{
Console.WriteLine($"保存成功:{data.Words}");
return await Task.FromResult(true);
}
}

  最后我们需要将我们的服务接入到WebApplication的map里,怎么做呢?首先我们需要定义一套代理类型用来反射并获取到具体的服务类型。这里为了简单的演示,我只设计包含一个入参和没有入参的情况下:

    public abstract class DynamicPorxy
{
public abstract Delegate Instance { get; set; }
}
public class DynamicPorxyImpl<Tsvc, Timpl, Tinput, Toutput> : DynamicPorxy where Timpl : class where Tinput : class where Toutput : class
{
public override Delegate Instance { get; set; }
public DynamicPorxyImpl(MethodInfo method)
{
Instance = ([FromServices] IServiceProvider sp, Tinput input) => ExpressionTool.CreateMethodDelegate<Timpl, Tinput, Toutput>(method)(sp.GetService(typeof(Tsvc)) as Timpl, input);
}
}
public class DynamicPorxyImpl<Tsvc, Timpl, Toutput> : DynamicPorxy where Timpl : class where Toutput : class
{
public override Delegate Instance { get; set; }
public DynamicPorxyImpl(MethodInfo method)
{
Instance = ([FromServices] IServiceProvider sp) => ExpressionTool.CreateMethodDelegate<Timpl, Toutput>(method)(sp.GetService(typeof(Tsvc)) as Timpl);
}
}

  接着我们创建一个代理工厂用于创建服务的方法委托并创建代理类型实例返回给调用端

    public class DynamicPorxyFactory
{
public static IEnumerable<(WebRouter, DynamicPorxy)> RegisterDynamicPorxy()
{
foreach (var methodinfo in DependencyContext.Default.CompileLibraries.Where(x => !x.Serviceable && x.Type != "package")
.Select(x => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(x.Name)))
.SelectMany(x => x.GetTypes().Where(x => !x.IsInterface && x.GetInterfaces().Any()).SelectMany(x => x.GetMethods().Where(y => y.CustomAttributes.Any(z => z.AttributeType == typeof(WebRouter))))))
{
var webRouter = methodinfo.GetCustomAttributes(typeof(WebRouter), false).FirstOrDefault() as WebRouter;
DynamicPorxy dynamicPorxy;
if (methodinfo.GetParameters().Any())
dynamicPorxy = Activator.CreateInstance(typeof(DynamicPorxyImpl<,,,>).MakeGenericType(methodinfo.DeclaringType.GetInterfaces()[0], methodinfo.DeclaringType, methodinfo.GetParameters()[0].ParameterType , methodinfo.ReturnType), new object[] { methodinfo }) as DynamicPorxy;
else
dynamicPorxy = Activator.CreateInstance(typeof(DynamicPorxyImpl<,,>).MakeGenericType(methodinfo.DeclaringType.GetInterfaces()[0], methodinfo.DeclaringType, methodinfo.ReturnType), new object[] { methodinfo }) as DynamicPorxy;
yield return (webRouter, dynamicPorxy);
}
}
}
    internal class ExpressionTool
{
internal static Func<TObj, Tin, Tout> CreateMethodDelegate<TObj, Tin, Tout>(MethodInfo method)
{
var mParameter = Expression.Parameter(typeof(TObj), "m");
var pParameter = Expression.Parameter(typeof(Tin), "p");
var mcExpression = Expression.Call(mParameter, method, Expression.Convert(pParameter, typeof(Tin)));
var reExpression = Expression.Convert(mcExpression, typeof(Tout));
return Expression.Lambda<Func<TObj, Tin, Tout>>(reExpression, mParameter, pParameter).Compile();
}
internal static Func<TObj, Tout> CreateMethodDelegate<TObj, Tout>(MethodInfo method)
{
var mParameter = Expression.Parameter(typeof(TObj), "m");
var mcExpression = Expression.Call(mParameter, method);
var reExpression = Expression.Convert(mcExpression, typeof(Tout));
return Expression.Lambda<Func<TObj, Tout>>(reExpression, mParameter).Compile();
}
}

  最后我们创建WebApplication的扩展方法来调用代理工厂以及注入IOC容器:

    public static class WebApplicationBuilderExtension
{
static Func<string, Delegate, IEndpointConventionBuilder> GetWebApplicationMap(HttpMethod httpMethod, WebApplication webApplication) => (httpMethod) switch
{
(HttpMethod.Get) => webApplication.MapGet,
(HttpMethod.Post) => webApplication.MapPost,
(HttpMethod.Put) => webApplication.MapPut,
(HttpMethod.Delete) => webApplication.MapDelete,
_ => webApplication.MapGet
};
public static WebApplication RegisterDependencyAndMapDelegate(this WebApplicationBuilder webApplicationBuilder, Action<IServiceCollection> registerDependencyAction, Func<IEnumerable<(WebRouter webRouter, DynamicPorxy dynamicPorxy)>> mapProxyBuilder)
{
webApplicationBuilder.Host.ConfigureServices((ctx, services) =>
{
registerDependencyAction(services);
});
var webApplication = webApplicationBuilder.Build();
mapProxyBuilder().ToList().ForEach(item => GetWebApplicationMap(item.webRouter.httpMethod, webApplication)(item.webRouter.path, item.dynamicPorxy.Instance));
return webApplication;
}
}

  当然包括我们的自定义容器注入方法:

    public class MyServiceDependency
{
public static void Register(IServiceCollection services)
{
services.AddScoped<IMyService, MyService>();
services.AddScoped<IMyRepository, MyRepository>();
}
}

  最后改造我们的program.cs的代码,通过扩展来注入容器和代理委托并最终生成路由-终结点:

await WebApplication.CreateBuilder().RegisterDependencyAndMapDelegate(MyServiceDependency.Register,DynamicPorxyFactory.RegisterDynamicPorxy).RunAsync("http://*:80");

  这样这套小型API系统就基本完成了,可以满足日常的依赖注入和独立的业务单元类型编写,最后我们启动并调用一下,可以看到确实否符合我们的预期成功的调用到了应用服务并且仓储也被正确的执行了:

使用.net6 WebApplication打造最小API的更多相关文章

  1. 从 MVC 到使用 ASP.NET Core 6.0 的最小 API

    从 MVC 到使用 ASP.NET Core 6.0 的最小 API https://benfoster.io/blog/mvc-to-minimal-apis-aspnet-6/ 2007 年,随着 ...

  2. 基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

    我前面几篇随笔介绍了关于几篇关于SqlSugar的基础封装,已经可以直接应用在Winform项目开发上,并且基础接口也通过了单元测试,同时测试通过了一些Winform功能页面:本篇随笔继续深化应用开发 ...

  3. 利用koa打造restful API

    概述 最近学习利用koa搭建API接口,小有所得,现在记录下来,供以后开发时参考,相信对其他人也有用. 就目前我所知道的而言,API有2种,一种是jsonp这种API,前端通过ajax来进行跨域请求获 ...

  4. Spring Boot入门系列(二十)快速打造Restful API 接口

    spring boot入门系列文章已经写到第二十篇,前面我们讲了spring boot的基础入门的内容,也介绍了spring boot 整合mybatis,整合redis.整合Thymeleaf 模板 ...

  5. 使用.NET6打造动态API

    ApiLite是直接将Service层自动生成api路由,可以不用添加Controller,支持模块插件化,在项目开发中能够提高工作效率,降低代码量. 开发环境 .NET SDK 6.0.100-rc ...

  6. 利用koa打造jsonp API

    概述 最近学习利用koa搭建API接口,小有所得,现在记录下来,供以后开发时参考,相信对其他人也有用. 就目前我所知道的而言,API有2种,一种是jsonp这种API,前端通过ajax来进行跨域请求获 ...

  7. 爬虫+django,打造个性化API接口

    简述 今天也是同事在做微信小程序的开发,需要音乐接口的测试,可是用网易云的开放接口比较麻烦,也不能进行测试,这里也是和我说了一下,所以就用爬虫写了个简单网易云歌曲URL的爬虫,把数据存入mysql数据 ...

  8. 尝新体验ASP.NET Core 6预览版本中发布的最小Web API(minimal APIS)新特性

    本文首发于<尝新体验ASP.NET Core 6预览版本中发布的最小Web API(minimal APIS)新特性> 概述 .NET开发者们大家好,我是Rector. 几天前(美国时间2 ...

  9. 性能再提升70%?大咖前瞻带你揭开.NET6的神秘面纱!

    本月初微软官宣.NET 6 的RC1即将在11月正式发布,这意味着.NET6正式版跟我们见面的时间又近了一步.在之前的.NET6预览版本中,微软加入了大量新功能特性,而在最终版本中将不再额外加入新的内 ...

随机推荐

  1. linux中级之keepalived概念

    一.HA集群中的相关术语 1.节点(node) 运行HA进程的一个独立主机,称为节点,节点是HA的核心组成部分,每个节点上运行着操作系统和高可用软件服务,在高可用集群中,节点有主次之分,分别称之为主节 ...

  2. 3 当某个应用的CPU使用达到100%,该怎么办?

    你最常用什么指标来描述系统的 CPU 性能呢?我想你的答案,可能不是平均负载,也不是 CPU 上下文切换,而是另一个更直观的指标-- CPU 使用率.CPU 使用率是单位时间内 CPU 使用情况的统计 ...

  3. 西门子S7系列以太网通讯处理器安装调式操作

    北京华科远创科技有限研发的远创智控ETH-YC模块,PLC转以太网型号有MPI-ETH-YC01和MPI-ETH-YC01,适用于西门子S7-200/S7-300/S7-400.SMART S7-20 ...

  4. nginx的请求处理

      nginx的请求处理¶ nginx使用一个多进程模型来对外提供服务,其中一个master进程,多个worker进程.master进程负责管理nginx本身和其他worker进程. 所有实际上的业务 ...

  5. Deformable 可变形的DETR

    Deformable 可变形的DETR This repository is an official implementation of the paper Deformable DETR: Defo ...

  6. MindSpore API编程概述

    MindSpore API编程概述 总体架构 MindSpore是一个全场景深度学习框架,旨在实现易开发.高效执行.全场景覆盖三大目标,其中易开发表现为API友好.调试难度低,高效执行包括计算效率.数 ...

  7. 多加速器驱动AGX的目标检测与车道分割

    多加速器驱动AGX的目标检测与车道分割 Object Detection and Lane Segmentation Using Multiple Accelerators with DRIVE AG ...

  8. 大规模数据处理Apache Spark开发

    大规模数据处理Apache Spark开发 Spark是用于大规模数据处理的统一分析引擎.它提供了Scala.Java.Python和R的高级api,以及一个支持用于数据分析的通用计算图的优化引擎.它 ...

  9. MySQL笔记02(黑马)

    DDL操作数据库.表 操作数据库:CRUD C(Create):创建 创建数据库: create database 数据库名称; 创建数据库,判断不存在,再创建: create database if ...

  10. mybatis学习——类型别名(typeAliases)

    为什么要用类型别名? 答:类型别名可为 Java 类型设置一个缩写名字. 它仅用于 XML 配置,意在降低冗余的全限定类名书写. 举个例子说明: 在我们编写映射文件的时候: <?xml vers ...