无锡企业网站制作需要多少钱,可以做淘宝推广的网站,做饮食找工作哪个网站好,网站建设工程前言随着.Net6的发布#xff0c;微软也改进了对之前ASP.NET Core构建方式#xff0c;使用了新的Minimal API模式。之前默认的方式是需要在Startup中注册IOC和中间件相关#xff0c;但是在Minimal API模式下你只需要简单的写几行代码就可以构建一个ASP.NET Core的Web应用微软也改进了对之前ASP.NET Core构建方式使用了新的Minimal API模式。之前默认的方式是需要在Startup中注册IOC和中间件相关但是在Minimal API模式下你只需要简单的写几行代码就可以构建一个ASP.NET Core的Web应用真可谓非常的简单加之配合c#的global using和Program的顶级声明方式使得Minimal API变得更为简洁不得不说.NET团队在,NET上近几年真是下了不少功夫接下来我们就来大致介绍下这种极简的使用模式。使用方式既然说它很简单了,到底是怎么个简单法呢。相信下载过Visual Studio 2022的同学们已经用它新建过ASP.NET Core 6的项目了默认的方式就是Minimal API模式这样让整个Web程序的结构看起来更简单了加上微软对Lambda的改进使其可以对Lambda参数进行Attribute标记有的场景甚至可以放弃去定义Controller类了。几行代码构建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字样。更改监听地址如果你想更改它监听的服务端口可以使用如下的方式进行更改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:9999日志操作日志操作也是比较常用的操作在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();基础环境配置无论我们在之前的.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_ENVIRONMENT•ASPNETCORE_CONTENTROOT•ASPNETCORE_APPLICATIONNAME主机相关设置我们在之前的.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();默认容器替换很多时候我们在使用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();中间件相关相信大家都已经仔细看过了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的扩展方法我们这里简单列举一下 |中间件名称 |描述|API| |--|--|--| |Authentication|认证中间件 | app.UseAuthentication() |Authorization|授权中间件. | app.UseAuthorization() |CORS|跨域中间件. | app.UseCors() |Exception Handler| 全局异常处理中间件. | app.UseExceptionHandler() |Forwarded Headers| 代理头信息转发中间件. | app.UseForwardedHeaders() |HTTPS Redirection|Https重定向中间件. | app.UseHttpsRedirection() |HTTP Strict Transport Security (HSTS)|特殊响应头的安全增强中间件. | app.UseHsts() |Request Logging|HTTP请求和响应日志中间件. | app.UseHttpLogging() |Response Caching|输出缓存中间件. | app.UseResponseCaching() |Response Compression|响应压缩中间件. | app.UseResponseCompression() |Session|Session中间件 | app.UseSession() |Static Files|静态文件中间件. | app.UseStaticFiles(), app.UseFileServer() |WebSockets|WebSocket支持中间件. | app.UseWebSockets()请求处理我们可以使用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相关的代码但是输出的却是委托里的内容因为我们上面示例中的委托都满足Funcstring的形式所以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};}
}其实本质都是一样的那就是将他们转换为可执行的委托无论什么样的形式能满足委托的条件即可。路由约束Minimal Api还支持在对路由规则的约束这个和我们之前使用UseEndpoints的方式类似比如我约束路由参数只能为整型如果不满足的话会返回404app.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 | 整数值必须大于18 | | max(value) | {age:max(120)} | 119 | 整数值必须小于120 | | range(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 | 请求信息必须包含该参数|模型绑定在我们之前使用ASP.NET Core Controller方式开发的话模型绑定是肯定会用到的它的作用就是简化我们解析Http请求信息也是MVC框架的核心功能它可以将请求信息直接映射成c#的简单类型或者POCO上面。在Minimal Api的Map{HTTPMethod}相关方法中同样可以进行丰富的模型绑定操作,目前可以支持的绑定源有如下几种•Route(路由参数)•QueryString•Header•Body(比如JSON)•Services(即通过IServiceCollection注册的类型)•自定义绑定绑定示例接下来我们首先看一下绑定路由参数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|路由参数| |age|querystring| |person|依赖注入|不仅仅如此它还支持更复杂的方式这使得模型绑定更为灵活比如以下示例app.MapPost(/goods,(Goods goods, Person person) ${person.Name}添加商品{goods.GName}成功);它的模型绑定的值来源可以是 |参数| 绑定来源| |--|--| |goods|body里的json| |person|依赖注入|当然如果你想让模型绑定的来源更清晰或者就想指定具体参数的绑定来源那也是可以的反正就是各种灵活比如上面的示例改造一下这样就可以显示声明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}));自定义绑定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中的结果输出总结起来其实可以分为三种情况1.IResult 结果输出,可以包含任何值得输出包含异步任务TaskIResult和ValueTaskIResult2.string 文本类型输出包含异步任务Taskstring和ValueTaskstring3.T 对象类型输出比如自定义的实体、匿名对象等包含异步任务 TaskT和ValueTaskT接下来简单演示几个例子来简单看一下具体是如何操作的首先最简单的就是输出文本类型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 { MessageHello 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方法。总结 本文我们主要是介绍了ASP.NET Core 6 Minimal API的常用的使用方式相信大家对此也有了一定的了解在.NET6中也是默认的项目方式整体来说却是非常的简单、简洁、强大、灵活不得不说Minimal API却是在很多场景都非常适用的。当然我也在其它地方看到过关于它的评价褒贬不一吧笔者认为没有任何一种技术是银弹存在即合理。如果你的项目够规范够合理那么使用Minimal API绝对够用如果不想用或者用不了也没关系能实现你想要结果就好了其实也没啥好评价的。