.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. 本文介绍使用windows系统自带的远程桌面mstsc连接Centos 7.x远程桌面的基本方法。

    本文介绍使用windows系统自带的远程桌面mstsc连接Centos 7.x远程桌面的基本方法. 一.前言 我希望用windows远程访问centos图形界面.xmanager连接centos远程桌 ...

  2. 程序"三高"解决方案

    0. 程序三高 1. 缓存 2. 预处理和延后处理 3. 池化 3.1 内存池 3.2 线程池 3.3 连接池 4. 异步(回调) 5. 消息队列 5.1 服务解耦 5.2 异步处理 5.3 流量削峰 ...

  3. Python判断身份证是否合法

    利用正则表达式实现对身份证合法程度的判断 1 # !usr/bin/env python3 2 # coding:utf-8 3 """ 4 @ Copyright (c ...

  4. 直击Huawei Mate 40产线背后的华为云IoT智能制造

    摘要:数字孪生?在数字世界找到物理世界的设备! 本文分享自华为云社区<[云驻共创]Huawei Mate 40产线直击之华为云IoT智能制造助力工厂数字化转型>,原文作者:启明. Part ...

  5. Python+Selenium - iframe定位

    元素在iframe中.在html当中,内嵌了另一个html (iframe) 分辨元素是否在iframe当中 在代码当中,从当前的html切换到iframe当中的html,然后在元素定位 切换方式:d ...

  6. eclipse自动格式化代码

    前段时间在Eclipse里面设置了java文件保存时自动格式化,在java->Code Style->Formatter里设置了自定义的格式化的样式,这样每次保存后都会自动格式化代码,用了 ...

  7. JAVA并发(6)-并发队列ArrayBlockingQueue

    本文讲ArrayBlockingQueue 1. 介绍 一个基于数组的有界阻塞队列,FIFO顺序.支持等待消费者和生产者线程的可选公平策略(默认是非公平的).公平的话通常会降低吞吐量,但是可以减少可变 ...

  8. P1828 [USACO3.2]香甜的黄油 Sweet Butter

    题目描述 农夫$John$发现做出全威斯康辛州最甜的黄油的方法:糖.把糖放在一片牧场上,他知道$N(1\leqslant N\leqslant 500)$只奶牛会过来舔它,这样就能做出能卖好价钱的超甜 ...

  9. 不会 Web 开发,也能让数据“动”起来的开源项目!

    本文面向有 Python 基础的小伙伴,有 Web 基础的更好 作者:HelloGitHub-吱吱 这里是 HelloGitHub 推出的<讲解开源项目>系列,今天要向小伙伴们介绍的是一个 ...

  10. 数据库创建好之后如何创建scott用户

    SQL> conn / as sysdba Connected. SQL> @%oracle_home%\rdbms\admin\utlsampl.sql 建立完成以后会自动退出sqlpl ...