济南建设项目竣工验收公示网站,旅游网站开发的流程图,西安全网推广公司,wordpress信用卡支付宝随着 .Net6 的发布#xff0c;微软也改进了对之前 ASP.NET Core 构建方式#xff0c;使用了新的 Minimal API 模式。以前默认的方式是需要在 Startup 中注册 IOC 和中间件相关#xff0c;但是在 Minimal API 模式下你只需要简单的写几行代码就可以构建一个 ASP.NET Core的We…
随着 .Net6 的发布微软也改进了对之前 ASP.NET Core 构建方式使用了新的 Minimal API 模式。以前默认的方式是需要在 Startup 中注册 IOC 和中间件相关但是在 Minimal API 模式下你只需要简单的写几行代码就可以构建一个 ASP.NET Core的Web 应用可谓非常的简单加之配合 c# 的 global using 和 Program 的顶级声明方式使得 Minimal API 变得更为简洁不得不说 .NET 团队在 .NET 上近几年下了不少功夫接下来我们就来大致介绍下这种极简的使用模式。 1. 使用方式 使用 Visual Studio 2022 新建的 ASP.NET Core 6 的项目默认的方式就是 Minimal API 模式整个 Web 程序的结构看起来更加简单再i加上微软对 Lambda 的改进使其可以对 Lambda 参数进行 Attribute 标记有的场景甚至可以放弃去定义 Controller 类了。 2. 几行代码构建Web程序 使用 Minimal API 最简单的方式就是能通过三行代码就可以构建一个 WebApi 的程序代码如下 var app WebApplication.Create(args);
app.MapGet(/, () Hello World);
app.Run();是的你没有看错仅仅这样运行起来就可以默认监听的 http://localhost:5000 和 https://localhost:5001所以直接在浏览器输入http://localhost:5000地址就可以看到浏览器输出Hello World字样。 3. 更改监听地址 如果你想更改它监听的服务端口可以使用如下的方式 var app WebApplication.Create(args);
app.MapGet(/, () Hello World);
app.Run(http://localhost:6666);如果想同时监听多个端口的话可以使用如下的方式 var app WebApplication.Create(args);
app.Urls.Add(http://localhost:6666);
app.Urls.Add(http://localhost:8888);
app.MapGet(/, () Hello World);
app.Run();或者是直接通过环境变量的方式设置监听信息设置环境变量ASPNETCORE_URLS的值为完整的监听URL地址这样的话就可以直接省略了在程序中配置相关信息了。 ASPNETCORE_URLShttp://localhost:6666如果设置多个监听的URL地址的话可以在多个地址之间使用分号;隔开多个值 ASPNETCORE_URLShttp://localhost:6666;https://localhost:8888如果想监听本机所有Ip地址可以使用如下方式 var app WebApplication.Create(args);
app.Urls.Add(http://*:6666);
app.Urls.Add(http://:8888);
app.Urls.Add(http://0.0.0.0:9999);
app.MapGet(/, () Hello World);
app.Run();同样的也可以使用添加环境变量的方式添加监听地址 ASPNETCORE_URLShttp://*:6666;https://:8888;http://0.0.0.0:99994. 日志操作 日志操作也是比较常用的操作在Minimal API中微软干脆把它提出来直接简化了操作如下所示 var builder WebApplication.CreateBuilder(args);
builder.Logging.AddJsonConsole();
var app builder.Build();
app.Logger.LogInformation(读取到的配置信息:{content}, builder.Configuration.GetSection(consul).GetConsulOption());
app.Run();5. 基础环境配置 无论我们在之前的.Net Core开发或者现在的.Net6开发都有基础环境的配置它包括 ApplicationName、ContentRootPath、 EnvironmentName相关不过在Minimal API中可以通过统一的方式去配置 var builder WebApplication.CreateBuilder(new WebApplicationOptions
{ApplicationName typeof(Program).Assembly.FullName,ContentRootPath Directory.GetCurrentDirectory(),EnvironmentName Environments.Staging
});Console.WriteLine($应用程序名称: {builder.Environment.ApplicationName});
Console.WriteLine($环境变量: {builder.Environment.EnvironmentName});
Console.WriteLine($ContentRoot目录: {builder.Environment.ContentRootPath});var app builder.Build();或者是通过环境变量的方式去配置最终实现的效果都是一样的。 ASPNETCORE_ENVIRONMENTASPNETCORE_CONTENTROOTASPNETCORE_APPLICATIONNAME6. 主机相关设置 我们在之前的.Net Core开发模式中程序的启动基本都是通过构建主机的方式比如之前的Web主机或者后来的泛型主机在Minimal API中同样可以进行这些操作,比如我们模拟一下之前泛型主机配置Web程序的方式 var builder WebApplication.CreateBuilder(args);
builder.Host.ConfigureDefaults(args).ConfigureWebHostDefaults(webBuilder
{webBuilder.UseStartupStartup();
});var app builder.Build();如果只是配置Web主机的话Minimal API还提供了另一种更直接的方式如下所示 var builder WebApplication.CreateBuilder(args);
builder.WebHost.UseStartupStartup();
builder.WebHost.UseWebRoot(webroot);var app builder.Build();7. 默认容器替换 很多时候我们在使用IOC的时候会使用其他三方的IOC框架比如大家耳熟能详的Autofac我们之前也介绍过其本质方式就是使用UseServiceProviderFactory中替换容器的注册和服务的提供在Minimal API中可以使用如下的方式去操作 var builder WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
//之前在Startup中配置ConfigureContainer可以使用如下方式
builder.Host.ConfigureContainerContainerBuilder(builder builder.RegisterModule(new MyApplicationModule()));var app builder.Build();8. 中间件相关 相信大家都已经仔细看过了WebApplication.CreateBuilder(args).Build()通过这种方式构建出来的是一个WebApplication类的实例而WebApplication正是实现了 IApplicationBuilder接口。所以其本质还是和我们之前使用Startup中的Configure方法的方式是一致的比如我们配置一个Swagger程序为例 var builder WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();var app builder.Build();
//判断环境变量
if (app.Environment.IsDevelopment())
{//异常处理中间件app.UseDeveloperExceptionPage();app.UseSwagger();app.UseSwaggerUI();
}
//启用静态文件
app.UseStaticFiles();app.UseAuthorization();
app.MapControllers();app.Run();常用的中间件配置还是和之前是一样的因为本质都是IApplicationBuilder的扩展方法我们这里简单列举一下 中间件名称描述APIAuthentication认证中间件app.UseAuthentication()Authorization授权中间件.app.UseAuthorization()CORS跨域中间件.app.UseCors()Exception Handler全局异常处理中间件.app.UseExceptionHandler()Forwarded Headers代理头信息转发中间件.app.UseForwardedHeaders()HTTPS RedirectionHttps重定向中间件.app.UseHttpsRedirection()HTTP Strict Transport Security (HSTS)特殊响应头的安全增强中间件.app.UseHsts()Request LoggingHTTP请求和响应日志中间件.app.UseHttpLogging()Response Caching输出缓存中间件.app.UseResponseCaching()Response Compression响应压缩中间件.app.UseResponseCompression()SessionSession中间件app.UseSession()Static Files静态文件中间件.app.UseStaticFiles(), app.UseFileServer()WebSocketsWebSocket支持中间件.app.UseWebSockets()9. 请求处理 我们可以使用WebApplication中的Map{HTTPMethod}相关的扩展方法来处理不同方式的Http请求比如以下示例中处理Get、Post、Put、Delete相关的请求 app.MapGet(/, () Hello GET);
app.MapPost(/, () Hello POST);
app.MapPut(/, () Hello PUT);
app.MapDelete(/, () Hello DELETE);如果想让一个路由地址可以处理多种Http方法的请求可以使用MapMethods方法如下所示 app.MapMethods(/multiple, new[] { GET, POST,PUT,DELETE }, (HttpRequest req) $Current Http Method Is {req.Method} );通过上面的示例我们不仅看到了处理不同Http请求的方式还可以看到Minimal Api可以根据委托的类型自行推断如何处理请求比如上面的示例我们没有写Response Write相关的代码但是输出的却是委托里的内容因为我们上面示例中的委托都满足Func的形式所以Minimal Api自动处理并输出返回的信息其实只要满足委托类型的它都可以处理接下来咱们来简单一下,首先是本地函数的形式 static string LocalFunction() This is local function;
app.MapGet(/local-fun, LocalFunction);还可以是类的实例方法 HelloHandler helloHandler new HelloHandler();
app.MapGet(/instance-method, helloHandler.Hello);class HelloHandler
{public string Hello(){return Hello World;}
}亦或者是类的静态方法 app.MapGet(/static-method, HelloHandler.SayHello);class HelloHandler
{public static string SayHello(string name){return $Hello {name};}
}其实本质都是一样的那就是将他们转换为可执行的委托无论什么样的形式能满足委托的条件即可。 10. 路由约束 Minimal Api还支持在对路由规则的约束这个和我们之前使用UseEndpoints的方式类似比如我约束路由参数只能为整型如果不满足的话会返回404。 app.MapGet(/users/{userId:int}, (int userId) $user id is {userId});
app.MapGet(/user/{name:length(20)}, (string name) $user name is {name});经常使用的路由约束还有其他几个,也不是很多大概有如下几种简单的列一下表格 限制示例匹配示例说明int{id:int}123456789, -123456789匹配任何整数bool{active:bool}true, false匹配 true 或 false. 忽略大小写datetime{dob:datetime}2016-12-31, 2016-12-31 7:32pm匹配满足DateTime类型的值decimal{price:decimal}49.99, -1,000.01匹配满足 decimal类型的值double{height:double}1.234, -1,001.01e8匹配满足 double 类型的值float{height:float}1.234, -1,001.01e8匹配满足 float 类型的值guid{id:guid}CD2C1638-1638-72D5-1638-DEADBEEF1638匹配满足Guid类型的值long{ticks:long}123456789, -123456789匹配满足 long 类型的值minlength(value){username:minlength(4)}KOBE字符串长度必须是4个字符maxlength(value){filename:maxlength(8)}CURRY字符串长度不能超过8个字符length(length){filename:length(12)}somefile.txt字符串的字符长度必须是12个字符length(min,max){filename:length(8,16)}somefile.txt字符串的字符长度必须介于8和l6之间min(value){age:min(18)}20整数值必须大于18max(value){age:max(120)}119整数值必须小于120range(min,max){age:range(18,120)}100整数值必须介于18和120之间alpha{name:alpha}Rick字符串必须由一个或多个a-z的字母字符组成且不区分大小写。regex(expression){ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}123-45-6789字符串必须与指定的正则表达式匹配。required{name:required}JAMES请求信息必须包含该参数11. 模型绑定 在我们之前使用ASP.NET Core Controller方式开发的话模型绑定是肯定会用到的它的作用就是简化我们解析Http请求信息也是MVC框架的核心功能它可以将请求信息直接映射成c#的简单类型或者POCO上面。在Minimal Api的Map{HTTPMethod}相关方法中同样可以进行丰富的模型绑定操作,目前可以支持的绑定源有如下几种 Route(路由参数)QueryStringHeaderBody(比如JSON)Services(即通过IServiceCollection注册的类型)自定义绑定1) 绑定示例 接下来我们首先看一下绑定路由参数 app.MapGet(/sayhello/{name}, (string name) $Hello {name});还可以使用路由和querystring的混用方式 app.MapGet(/sayhello/{name}, (string name,int? age) $my name is {name},age {age});这里需要注意的是,我的age参数加了可以为空的标识如果不加的话则必须要在url的请求参数中传递age参数否则将报错这个和我们之前的操作还是有区别的。 具体的类也可以进行模型绑定比如咱们这里定义了名为Goods的POCO进行演示 app.MapPost(/goods,(Goods goods)$商品{goods.GName}添加成功);class Goods
{public int GId { get; set; }public string GName { get; set; }public decimal Price { get; set; }
}需要注意的是HTTP方法GET、HEAD、OPTIONS、DELETE将不会从body进行模型绑定,如果需要在Get请求中获取Body信息可以直接从HttpRequest中读取它。 如果我们需要使用通过IServiceCollection注册的具体实例可以以通过模型绑定的方式进行操作(很多人喜欢叫它方法注入,但是严格来说却是是通过定义模型绑定的相关操作实现的)而且还简化了具体操作我们就不需要在具体的参数上进行FromServicesAttribute标记了。 var builder WebApplication.CreateBuilder(args);
builder.Services.AddScopedPerson(provider new() { Id 1, Name yi念之间, Sex Man });
var app builder.Build();app.MapGet(/, (Person person) $Hello {person.Name}!);
app.Run();如果是混合使用的话也可以不用指定具体的BindSource进行标记了前提是这些值的名称在不同的绑定来源中是唯一的这种感觉让我想到了刚开始学习MVC4.0的时候模型绑定的随意性比如下面的例子 app.MapGet(/sayhello/{name}, (string name,int? age,Person person) $my name is {name},age {age}, sex {person.Sex});上面示例的模型绑定参数来源可以是 参数绑定来源name路由参数agequerystringperson依赖注入不仅仅如此它还支持更复杂的方式这使得模型绑定更为灵活比如以下示例 app.MapPost(/goods,(Goods goods, Person person) ${person.Name}添加商品{goods.GName}成功);它的模型绑定的值来源可以是 参数绑定来源goodsbody里的jsonperson依赖注入当然如果你想让模型绑定的来源更清晰或者就想指定具体参数的绑定来源那也是可以的反正就是各种灵活比如上面的示例改造一下这样就可以显示声明
app.MapPost(/goods,([FromBody]Goods goods, [FromServices]Person person) ${person.Name}添加商品{goods.GName}成功);很多时候我们可能通过定义类和方法的方式来声明Map相关方法的执行委托这个时候呢依然可以进行灵活的模型绑定而且可能你也发现了直接通过lambda表达式的方式虽然支持可空类型但是它不支持缺省参数也就是咱们说的方法默认参数的形式比如 app.MapPost(/goods, GoodsHandler.AddGoods);class GoodsHandler
{public static string AddGoods(Goods goods, Person person, int age 20) ${person.Name}添加商品{goods.GName}成功;
}当然你也可以对AddGoods方法的参数进行显示的模型绑定处理十分的灵活。 public static string AddGoods([FromBody] Goods goods, [FromServices] Person person, [FromQuery]int age 20) ${person.Name}添加商品{goods.GName}成功;在使用Map相关方法的时候由于是在Program入口程序或者其他POCO中直接编写相关逻辑的因此需要用到HttpContext、HttpRequest、HttpResponse相关实例的时候没办法进行直接操作这个时候也需要通过模型绑定的方式获取对应实例 app.MapGet(/getcontext,(HttpContext context,HttpRequest request,HttpResponse response) response.WriteAsync($IP:{context.Connection.RemoteIpAddress},Request Method:{request.Method}));2) 自定义绑定 Minimal Api采用了一种新的方式来自定义模型绑定这种方式是一种基于约定的方式无需提前注册也无需集成什么类或者实现什么接口只需要在自定义的类中存在TryParse和BindAsync方法即可这两个方法的区别如下 TryParse方法是对路由参数、url参数、header相关的信息进行转换绑定 BindAsync可以对任何请求的信息进行转换绑定功能比TryParse要强大 接下来我们分别演示一下这两种方式的使用方法首先是TryParse方法 app.MapGet(/address/getarray,(Address address) address.Addresses);public class Address
{public Liststring? Addresses { get; set; }public static bool TryParse(string? addressStr, IFormatProvider? provider, out Address? address){var addresses addressStr?.Split(,, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);if (addresses ! null addresses.Any()){address new Address { Addresses addresses.ToList() };return true;}address new Address();return false;}
}这样就可以完成简单的转换绑定操作,从写法上我们可以看到TryParse方法确实存在一定的限制不过操作起来比较简单这个时候我们模拟请求 http://localhost:5036/address/getarray?address山东,山西,河南,河北请求完成会得到如下结果 [山东,山西,河南, 河北]然后我们改造一下上面的例子使用BindAsync的方式进行结果转换看一下它们操作的不同 app.MapGet(/address/getarray,(Address address) address.Addresses);public class Address
{public Liststring? Addresses { get; set; }public static ValueTaskAddress? BindAsync(HttpContext context, ParameterInfo parameter){string addressStr context.Request.Query[address];var addresses addressStr?.Split(,, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);Address address new();if (addresses ! null addresses.Any()){address.Addresses addresses.ToList();return ValueTask.FromResultAddress?(address);}return ValueTask.FromResultAddress?(address);}
}同样请求http://localhost:5036/address/getarray?address山东,山西,河南,河北 地址会得到和上面相同的结果到底如何选择同学们可以按需使用得到的效果都是一样的。如果类中同时存在TryParse和BindAsync方法那么只会执行BindAsync方法。 输出结果 相信通过上面的其他示例演示我们大概看到了一些在Minimal Api中的结果输出总结起来其实可以分为三种情况 IResult 结果输出,可以包含任何值得输出包含异步任务Task和ValueTask string 文本类型输出包含异步任务Task和ValueTask T 对象类型输出比如自定义的实体、匿名对象等包含异步任务 Task和ValueTask 接下来简单演示几个例子来简单看一下具体是如何操作的首先最简单的就是输出文本类型 app.MapGet(/hello, () Hello World);然后输出一个对象类型对象类型可以包含对象或集合甚至匿名对象或者是咱们上面演示过的HttpResponse对象这里的对象可以理解为面向对象的那个对象满足Response输出要求即可。 app.MapGet(/simple, () new { Message Hello World });//或者是app.MapGet(/array,()new string[] { Hello, World });//亦或者是EF的返回结果app.Map(/student,(SchoolContext dbContext,int classId)dbContext.Student.Where(ii.ClassIdclassId));还有一种是微软帮我们封装好的一种形式即返回的是IResult类型的结果,微软也是很贴心的为我们统一封装了一个静态的Results类方便我们使用简单演示一下这种操作 //成功结果app.MapGet(/success,() Results.Ok(Success));//失败结果app.MapGet(/fail, () Results.BadRequest(fail));//404结果app.MapGet(/404, () Results.NotFound());//根据逻辑判断返回app.Map(/student, (SchoolContext dbContext, int classId) {var classStudents dbContext.Student.Where(i i.ClassId classId);return classStudents.Any() ? Results.Ok(classStudents) : Results.NotFound();});上面我们也提到了Results类其实是微软帮我们多封装了一层它里面的所有静态方法都是返回IResult的接口实例这个接口有许多实现的类满足不同的输出结果比如Results.File(“foo.text”)方法本质就是返回一个FileContentResult类型的实例。 public static IResult File(byte[] fileContents,string? contentType null,string? fileDownloadName null,bool enableRangeProcessing false,DateTimeOffset? lastModified null,EntityTagHeaderValue? entityTag null) new FileContentResult(fileContents, contentType){FileDownloadName fileDownloadName,EnableRangeProcessing enableRangeProcessing,LastModified lastModified,EntityTag entityTag,};亦或者Results.Json(new { Message“Hello World” })本质就是返回一个JsonResult类型的实例。 public static IResult Json(object? data, JsonSerializerOptions? options null, string? contentType null, int? statusCode null) new JsonResult{Value data,JsonSerializerOptions options,ContentType contentType,StatusCode statusCode,};当然我们也可以自定义IResult的实例比如我们要输出一段html代码。 微软很贴心的为我们提供了专门扩展Results的扩展类IResultExtensions基于这个类我们才能完成IResult的扩展 static class ResultsExtensions{//基于IResultExtensions写扩展方法public static IResult Html(this IResultExtensions resultExtensions, string html){ArgumentNullException.ThrowIfNull(resultExtensions, nameof(resultExtensions));//自定义的HtmlResult是IResult的实现类return new HtmlResult(html);}}class HtmlResult:IResult{//用于接收html字符串private readonly string _html;public HtmlResult(string html){_html html;}/// summary/// 在该方法写自己的输出逻辑即可/// /summary/// returns/returnspublic Task ExecuteAsync(HttpContext httpContext){httpContext.Response.ContentType MediaTypeNames.Text.Html;httpContext.Response.ContentLength Encoding.UTF8.GetByteCount(_html);return httpContext.Response.WriteAsync(_html);}}定义完成这些我们就可以直接在Results类中使用我们定义的扩展方法了使用方式如下 app.MapGet(/hello/{name}, (string name) Results.Extensions.Html($htmlheadtitleIndex/title/headbodyh1Hello {name}/h1/body/html));这里需要注意的是我们自定义的扩展方法一定是基于IResultExtensions扩展的然后再使用的时候注意是使用的Results.Extensions这个属性因为这个属性是IResultExtensions类型的然后就是我们自己扩展的Results.Extensions.Html方法。 12. 总结 本文我们主要是介绍了ASP.NET Core 6 Minimal API的常用的使用方式相信大家对此也有了一定的了解在.NET6中也是默认的项目方式整体来说却是非常的简单、简洁、强大、灵活不得不说Minimal API却是在很多场景都非常适用的。当然我也在其它地方看到过关于它的评价褒贬不一吧笔者认为没有任何一种技术是银弹存在即合理。如果你的项目够规范够合理那么使用Minimal API绝对够用如果不想用或者用不了也没关系能最终实现需要的结果就好。 参考资料 C#教程编程宝库--------------------- 作者编程宝库 来源CSDN 原文https://blog.csdn.net/wanghao72214/article/details/121682606 版权声明本文为作者原创文章转载请附上博文链接 内容解析ByCSDN,CNBLOG博客文章一键转载插件