您的当前位置:首页正文

从MVC到使用ASP.NETCore6.0的MinimalApi

2021-02-16 来源:品趣旅游知识分享网
从MVC到使⽤ASP.NETCore6.0的MinimalApi

从 MVC 到使⽤ ASP.NET Core 6.0 的Minimal API

2007 年,随着 ASP.NET MVC 引⼊了其他语⾔中变得司空见惯的,并为其提供原⽣⽀持,.NET Web 应⽤程序开发有了极速的发展。2012 年,也许是由于 ReSTful API 的⽇益流⾏,借鉴了 ASP.NET MVC 的许多概念⼜引⼊了 ASP.NET Web API,这是对 WCF 的重⼤改进,使开发⼈员能够以更少的仪式构建 HTTP API,。

后来,在 ASP.NET Core 中,⽤于构建⽹站和 API 的单⼀框架,这些框架被统⼀到了 ASP.NET Core MVC 中。

在 ASP.NET Core MVC 应⽤程序中,控制器负责接受输⼊、执⾏或编排操作并返回响应。它是⼀个功能齐全的框架,通过过滤器、内置模型绑定和验证、约定和基于声明的⾏为等提供可扩展的管道。对于许多⼈来说,它是构建现代 HTTP 应⽤程序的多合⼀解决⽅案。在某些情况下,您可能只需要 MVC 框架的特定功能或具有使 MVC 不受欢迎的性能限制。随着更多 HTTP 功能作为 ASP.NET Core 中间件(例如⾝份验证、授权、路由等)出现,⽆需 MVC 即可构建轻量级 HTTP 应⽤程序变得更加容易,但通常需要⼀些功能,否则您必须⾃⼰构建,例如作为模型绑定和 HTTP 响应⽣成。

ASP.NET Core 6.0 旨在通过 Minimal API 弥合这⼀差距,以更少的仪式提供 ASP.NET MVC 的许多功能。这篇⽂章提供了有关如何将传统MVC 概念转换为这种构建轻量级 HTTP API 和服务的新⽅法的分步指南。

在这些⽰例中,我使⽤的是 .NET 6.0 预览 7,为了提供公平和最新的并排⽐较,我还使⽤了最新的webapi模板,因为 MVC 还受益于 C# 10的⼀些新特性,使事情变得更加“最⼩化”。

Startup

MVC

dotnet new webapi

新的 ASP.NET 模板取消了Startup类并利⽤了 C# 10 的顶级语句功能,因此我们有⼀个Program.cs包含所有引导代码的⽂件:

var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();var app = builder.Build();

// Configure the HTTP request pipeline.if (builder.Environment.IsDevelopment()){

app.UseDeveloperExceptionPage();}

app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();

调⽤builder.Services.AddControllers()负责注册 MVC 框架依赖项并发现我们的控制器。然后我们调⽤app.MapControllers()注册我们的控制器路由和MVC 中间件。

MinimalAPI

dotnet new web

ASP.NET Empty 模板对规范的“Hello world”⽰例使⽤ Minimal API:

var builder = WebApplication.CreateBuilder(args);var app = builder.Build();

if (app.Environment.IsDevelopment()){

app.UseDeveloperExceptionPage();}

app.MapGet(\"/\app.Run();

该MapGet⽅法是 Minimal API 扩展的⼀部分。除此之外,它与 MVC 并没有太⼤区别(考虑到 HTTPS 重定向和授权中间件只是从 Empty 模

板中省略⽽不是隐式启⽤)。

定义路由和处理程序

MVC

在 MVC 中,我们有定义路由的⽅法,⼀种是通过约定,⼀种是使⽤属性。

基于约定的路由更常⽤于⽹站⽽不是 API,并包含在mvc模板中。⽽不是app.MapControllers我们使⽤:

app.MapControllerRoute( name: \"default\

pattern: \"{controller=Home}/{action=Index}/{id?}\");

所述pattern指定路线的不同区段,并且允许指定的默认值。参数可以利⽤ ASP.NET 的来限制接受的值。对于 API,建议使⽤。

通过属性路由,您可以使⽤指定 HTTP 动词和路径的属性来装饰控制器和动作:

[ApiController]

[Route(\"[controller]\")]

public class WeatherForecastController : ControllerBase{

private static readonly string[] Summaries = new[] {

\"Freezing\ };

[HttpGet]

public IEnumerable Get() {

return Enumerable.Range(1, 5).Select(index => new WeatherForecast {

Date = DateTime.Now.AddDays(index),

TemperatureC = Random.Shared.Next(-20, 55),

Summary = Summaries[Random.Shared.Next(Summaries.Length)] })

.ToArray(); }}

在启动时,路由将⾃动注册。上⾯的⽰例来⾃默认webapi模板,演⽰了路由令牌替换。该[Route(\"[controller]\")]属性将使⽤/weatherforecast所有路由的前缀(或资源)(控制器类名减去“Controller”后缀),⽆参数[HttpGet]属性将在资源的根处注册操作,因此HTTP GET /weatherforecast将命中此操作。

如果我想扩展 API 以允许按位置检索预测,我可以添加以下操作:

[HttpGet(\"locations/{location}\")]

public IEnumerable GetByLocation(string location){}

请求时,/weatherforecast/locations/london该值london将绑定到相应的操作参数。

与它们的 Minimal API 对应物相⽐,MVC 控制器看起来⾮常臃肿。但是,值得注意的是,控制器也可以是 POCO(Plain Old CLRObjects)。为了获得与上⾯的“Hello World”最⼩ API ⽰例相同的结果,我们只需要:

public class RootController{

[HttpGet(\"/\")]

public string Hello() => \"Hello World\";}

从这⾥你可以看到尤其是当你考虑到你仍然需要⼀定程度的模块化时,即使使⽤Minimal API, MVC 也可以是“Minimal”,。

MinimalAPI

要使⽤ Minimal API 定义路由和处理程序,请使⽤Map(Get|Post|Put|Delete)⽅法。有趣的是没有MapPatch⽅法,但您可以使⽤MapMethods.要使⽤ Minimal API 实现相同的天⽓预报⽰例:

var summaries = new[]{

\"Freezing\};

app.MapGet(\"/weatherforecast\

{

return Enumerable.Range(1, 5).Select(index => new WeatherForecast {

Date = DateTime.Now.AddDays(index),

TemperatureC = Random.Shared.Next(-20, 55),

Summary = summaries[Random.Shared.Next(summaries.Length)] })

.ToArray();});app.Run();

与 MVC ⽰例类似,我们可以将其扩展为按位置查询:

app.MapGet(\"/weatherforecast/locations/{location}\{});

请注意,在 MVC 和 Minimal API ⽰例中,我们受益于返回类型到序列化 HTTP 200 (OK) 响应的隐式转换。稍后我们将介绍两个框架的更明确的 HTTP 对象模型。

模型绑定

模型绑定是从 HTTP 请求中检索值并将它们转换为 .NET 类型的过程。由于我们在上⾯介绍了绑定路由值,本节将主要关注在请求正⽂中或通过查询字符串参数接收 JSON 数据。

MVC

在 MVC 中,您可以将 JSON 从请求正⽂绑定到 .NET 类型,⽅法是将其作为参数传递给您的操作⽅法并使⽤[FromBody]属性对其进⾏修饰:

[HttpPost(\"/payments\")]

public IActionResult Post([FromBody]PaymentRequest request){ }

或者,通过使⽤[ApiController]属性装饰您的控制器,将应⽤⼀个约定来绑定主体中的任何复杂类型。

在某些情况下,您可能希望从查询参数绑定复杂类型。我喜欢为具有多个过滤选项的搜索端点执⾏此操作。您可以使⽤以下[FromQuery]属性实现此⽬的:

[HttpGet(\"/echo\")]

public IActionResult Search([FromQuery]SearchRequest request){ }

否则,简单类型将从路由或查询字符串值绑定:

[HttpGet(\"/portfolios/{id}\")]

public IActionResult Search(int id, int? page = 1, int? pageSize = 10){ }

/portfolios/10?page=2&pagesize=20将满⾜上述操作参数的请求。

上⾯的⽰例还通过将可选参数标记为可为空并可选地提供默认值来演⽰可选参数的使⽤。

这对于复杂类型的⼯作⽅式略有不同。即使将类型设为可空,如果未发送正⽂,您将收到 HTTP 415(⽆效媒体类型)或 400(错误请求)响应,具体取决于是否Content-Type设置了标头。

以前,这种⾏为只能通过全局进⾏MvcOptions.AllowEmptyInputInBodyModelBinding全局配置,但从 ASP.NET Core 5 开始,它现在可以按请求进⾏配置:

[HttpPost(\"/payments\")]

public IActionResult Post([FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]PaymentRequest? request){ }

MinimalAPI

Minimal API 中的模型绑定⾮常相似;您使⽤您希望从请求中绑定的类型配置您的处理程序委托。复杂类型将从请求正⽂中⾃动绑定,⽽简单类型将从路由或查询字符串参数中绑定。使⽤ Minimal API 实现的相同⽰例如下:

app.MapPost(\"/payments\

{ });

app.MapGet(\"/portfolios/{id}\{});

为了指定默认值,您需要传递⼀个⽅法作为委托,因为 C# 尚不⽀持内联 lambda 函数的默认值:

app.MapGet(\"/search/{id}\app.Run();

IResult Search(int id, int? page = 1, int? pageSize = 10){}

该[FromQuery]属性不⽀持绑定复杂类型。有可⽤于⾃定义模型绑定的扩展点,我将在后⾯的⽂章中介绍。要⽀持可选的请求参数,您可以应⽤与[FromBody]MVC相同的属性,指定EmptyBodyBehavior:

app.MapPost(\"/payments\{});

HTTP 响应

MVC 和 Minimal API 都会⾃动将您的返回类型序列化到响应正⽂并返回 HTTP 200 (OK) 响应,例如:

// MVC

[HttpPost(\"/echo\")]

public EchoRequest Echo(EchoRequest echo) => echo;// Minimal API

app.MapPost(\"/echo\

您还可以返回void或Task返回⼀个空的 HTTP 200 (OK) 响应:

// MVC

[HttpPost(\"/echo\")]

public void Echo(EchoRequest echo) => {};// Minimal API

app.MapPost(\"/echo\

除了隐式转换之外,MVC 和 Minimal API 都有⼀个丰富的 HTTP 响应对象模型,涵盖了最常见的 HTTP 响应。

MVC

在 MVC 中,您可以返回IActionResult并使⽤许多内置实现,例如AcceptedResult. 如果您是从ControllerBase那⾥派⽣控制器的,那么⼤多数响应类型都可以使⽤辅助⽅法:

[HttpDelete(\"/projects/{id}\")]

public IActionResult Delete(int id){

return Accepted();}

MinimalAPI

使⽤ Minimal API,我们可以返回IResult. 在Results静态类可以很容易地产⽣了⼀些内置的响应类型:

app.MapDelete(\"/projects/{id}\{

return Results.Accepted();});

依赖注⼊

MVC

要将依赖项注⼊ MVC 控制器,我们通常使⽤构造函数注⼊,其中所需的类型(或更常见的是它们的底层接⼝)作为构造函数参数提供:

public class CacheController : ControllerBase

{

private readonly ICache _cache; public CacheController(ICache cache) {

_cache = cache; }

[HttpDelete(\"/cache/{id}\")]

public async Task Delete(string id) {

await _cache.Delete(id); return Accepted(); }}

依赖项在启动时注册(现在默认在 Program.cs 中):

builder.Services.AddScoped();

使⽤范围⽣命周期注册的服务将在 MVC 应⽤程序中按 HTTP 请求创建。

MinimalAPI

使⽤ Minimal API,我们仍然可以从依赖注⼊中受益,但不是使⽤构造函数注⼊,⽽是在处理程序委托中将依赖作为参数传递:

app.MapDelete(\"/cache/{id}\{

await cache.Delete(id); return Results.Accepted();});

这种⽅法更纯粹,可以使测试更容易。不利的⼀⾯是,⼀旦您获得多个依赖项,您的处理程序定义就会变得⾮常嘈杂。

最后,虽然依赖在 内本地声明的依赖项可能很诱⼈Program.cs,但这不仅会使测试变得困难,⽽且还会导致范围问题。我建议尽可能利⽤ DI容器,即使是单例依赖。

HTTp上下⽂

您的 API 可能需要访问有关 HTTP 请求的其他信息,例如当前⽤户的标头或详细信息。MVC 和 Minimal API 都构建在您熟悉的相同ASP.NET Core HTTP 抽象之上。

MVC

在MVC中,获得您的控制器时,从ControllerBase您可以访问HttpContext,HttpRequest,HttpResponse和当前⽤户(ClaimsPrincipal从基类属性):

[HttpGet]

public IEnumerable Get(){

if (Request.Headers.TryGetValue(\"some header\ { }

bool isSpecialUser = User.Identity.IsAuthenticated && User.HasClaim(\"special\");

如果您的控制器是⼀个简单的 POCO 并且不是派⽣⾃ControllerBase您,则需要使⽤构造函数注⼊来注⼊IHttpContextAccessor您的控制器或直接访问请求、响应和⽤户,请为这些类型执⾏⼀些 DI 连接。如果 POCO 控制器可以利⽤类似于下⾯描述的 Minimal API 的⽅法注⼊,那就太好了。

MinimalAPI

使⽤ Minimal API,您可以通过将作为参数传递给处理程序委托来访问相同的上下⽂信息:

HttpContextHttpRequestHttpResponseClaimsPrincipal

CancellationToken (请求中⽌)app.MapGet(\"/hello\ return \"Hello \" + user.FindFirstValue(\"sub\");});

Url映射

在某些情况下,您需要⽣成指向 API 其他部分的链接。在 ASP.NET Core 中,我们可以依靠现有的 HTTP 和路由基础结构来避免对 URI 组件进⾏硬编码。要⽣成到已知路线的链接,我们⾸先需要⼀种⽅法来识别它们。

MVC

在 MVC 中,我们可以将⼀个Name属性传递给我们⽤来装饰控制器操作的路由属性,例如:

[HttpGet(\"products/{id}\public IActionResult GetProduct(int id){}

然后我们可以使⽤IUrlHelper⽣成指向该路由的链接:

[HttpPost(\"products\

public IActionResult CreateProduct(CreateProduct command){

var product = Create(command);

return Created(Url.Link(\"get_product\}

请注意路由的路由参数(get_product在本例中为 ID)是如何作为匿名对象传递的。

IUrlHelper可通过Url酒店获得ControllerBase。或者,您可以将它注⼊到您的类中,前提是您在HTTP 范围内。

MinimalAPI

使⽤ Minimal API,您可以通过附加元数据来命名端点:

app.MapGet(\"/products/{id}\{

return Results.Ok();})

.WithMetadata(new EndpointNameMetadata(\"get_product\"));

上述内容的简写版本WithName将在未来版本中提供。

还有⼀个是在传递⽅法组⽽不是内联 lambda 时隐式⽣成端点名称。从上⾯的问题:

// These endpoints have their name set automaticallyapp.MapGet(\"/todos/{id}\

async Task GetTodoById(int id, TodoDb db){

return await db.Todos.FindAsync(id) is Todo todo

Results.Ok(todo) : Results.NotFound();};

更新:David Fowler 确认这将在 .NET 6 rc1 中可⽤命名端点后,您可以注⼊LinkGenerator处理程序以⽣成链接:

app.MapPost(\"payments\{

var result = await mediator.Send(payment);

return result.Match(

invalidRequest => invalidRequest.ToValidationProblem(),

success => Results.Created(links.GetUriByName(httpContext, \"get_payment\ );})

⼀些内置的 Result 助⼿代表你处理这个样板。同样的例⼦,简化为Results.CreatedAtRoute:

app.MapPost(\"payments\{

var result = await mediator.Send(payment);

return result.Match(

invalidRequest => invalidRequest.ToValidationProblem(),

success => Results.CreatedAtRoute(\"get_payment\ );})

验证

MVC

输⼊验证是任何 API 的重要组成部分。MVC 在 ASP.NET 之上添加的功能之⼀是模型状态。从:

模型状态表⽰来⾃两个⼦系统的错误:模型绑定和模型验证。源⾃模型绑定的错误通常是数据转换错误。MVC 还包括对通过进⾏验证的内置⽀持,例如:

public class PaymentRequest{

[Required]

public int? Amount { get; set; }

[Required]

[StringLength(3)]

public string Currency { get; set; }}

提⽰:⼀个流⾏的选择是为替换基于默认属性的验证。

绑定到此模型类型时,任何验证错误都会⾃动添加到模型状态。在控制器中,我们可以检查它并采取适当的措施:

public IActionResult Post(PaymentRequest paymentRequest){

if (!ModelState.IsValid) {

// return validation error }

// otherwise process}

事实上,如果我们⽤[ApiController]约定来装饰我们的控制器,我们甚⾄不需要做上⾯的事情。这将过滤器应⽤于 MVC 管道,该过滤器将验证任何请求的输⼊并在必要时返回问题详细信息响应。

{

\"type\": \"https://tools.ietf.org/html/rfc7231#section-6.5.1\ \"title\": \"One or more validation errors occurred.\ \"status\": 400,

\"traceId\": \"00-293242b60c05924743847956126b31fe-a1b01281b398430d-00\ \"errors\": {

\"Amount\": [

\"The Amount field is required.\" ],

\"Currency\": [

\"The Currency field is required.\" ] }}

这是 MVC 过滤器管道如何从您的应⽤程序中删除重复的⼀个很好的例⼦。过滤器可以访问您在 ASP.NET 中间件中没有的其他上下⽂。这是允许内置验证中间件⾃动执⾏的原因,因为它能够在模型绑定发⽣后运⾏。

MinimalAPI

就⽬前⽽⾔,Minimal API 没有任何内置的验证⽀持。但是,您当然可以⾃由地推出⾃⼰的产品。Damian Edwards 创建了,这是⼀个利⽤类似于默认 MVC 验证的验证属性的⼩型库:

app.MapPost(\"/widgets\

!MinimalValidation.TryValidate(widget, out var errors) Results.BadRequest(errors)

: Results.Created($\"/widgets/{widget.Name}\app.Run();

class Widget{

[Required, MinLength(3)]

public string? Name { get; set; }

public override string? ToString() => Name;}

您可以找到更多⽰例。

我个⼈更喜欢使⽤通常⽤这个库替换 MVC 中基于属性的验证。下⾯是使⽤ Fluent Validation 和最少 API 的⽰例:

builder.Services.AddValidatorsFromAssemblyContaining(lifetime: ServiceLifetime.Scoped);var app = builder.Build();

app.MapPost(\"payments\{

ValidationResult validationResult = validator.Validate(paymentRequest); if (!validationResult.IsValid) {

return Results.ValidationProblem(validationResult.ToDictionary()); }

// otherwise process});

// URL generation?app.Run();

public record PaymentRequest(int? Amount, string Currency){

public class Validator : AbstractValidator {

public Validator() {

RuleFor(x => x.Amount).NotNull().WithMessage(\"amount_required\"); RuleFor(x => x.Currency).Length(3).WithMessage(\"currency_invalid\"); } }}

public static class ValidationExtensions{

public static IDictionary ToDictionary(this ValidationResult validationResult) => validationResult.Errors

.GroupBy(x => x.PropertyName) .ToDictionary( g => g.Key,

g => g.Select(x => x.ErrorMessage).ToArray() );}

注意:FV 验证器不需要嵌套在它们的⽬标类型中。这只是个⼈喜好。

在这⾥,我利⽤ Fluent Validation 的程序集扫描功能来定位我的验证器。或者,我可以IValidator显式注册实现。⽆论哪种⽅式,这都意味着我的验证器可以提供给我的处理程序,我可以验证传⼊的类型。

这⾥的⼀个缺点是您可能最终会在每个处理程序中编写相同的样板验证检查。可以通过⼀些重构来减少它,但是没有可以访问绑定模型的预处理程序钩⼦,我们不能像使⽤ MVC 过滤器那样轻松地短路请求。我将在稍后的博客⽂章中介绍⼀些替代⽅法。

JSON 序列化

您可能需要⾃定义默认的 JSON 序列化设置以满⾜您的需求或 API 样式指南。例如,默认设置将字段名称序列化为驼峰式⼤⼩写(即firstName),但我们的 API 标准要求所有 API 都使⽤蛇形⼤⼩写(即first_name)。ASP.NET 6.0 使⽤ System.Text.Json 处理 JSON,⾃定义选项在有详细说明。

MVC

在 MVC 中,您可以通过AddJsonOptions扩展⾃定义 JSON :

services.AddControllers()

.AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = new SnakeCaseNamingPolicy());

注意:开箱即⽤仍不⽀持蛇形命名法(,译者按:当今许多编程语⾔都建议在某些情况下使⽤类似蛇的命名法,对于单个字符或单词(例如A,PYTHON,BOY),当将它们⽤作变量名时,⼤致所有⼩写字母,全部⼤写字母和⾸字母⼤写字母。但是,编程语⾔通常需要使⽤多个单词或符号来表⽰变量名中更丰富的含义。 英语习惯于使⽤空格分隔单词,但是这种⽤法会给编程语⾔带来⿇烦,因此程序员创建了其他⽅法,蛇形命名法就是⽤下划线分隔两个字符,使其可读性更强)。您可以在找到上述策略的源代码。

Minimal API

Minimal API 依赖于许多来序列化到/从 JSON。它们允许JsonSerializerOptions提供,但否则会退回到JsonOptions从HttContext.Request.Services. 您可以在启动时配置这些选项:

builder.Services.Configure(opt =>{

opt.SerializerOptions.PropertyNamingPolicy = new SnakeCaseNamingPolicy());

});

注意,你需要配置的Microsoft.AspNetCore.Http.Json.JsonOptions不是Mvc命名空间下的类。

我在深⼊研究源代码时发现的⼀件事是,序列化对象的IResult实现的基类仅⽀持序列化 JSON。有⼈告诉我这是设计使然,因为⼤多数开发⼈员很少需要⽀持其他媒体类型。如果您需要⽀持内容协商,您可能需要构建⾃⼰的IResult.

授权

我想介绍的最后⼀个功能是授权。⾝份验证和授权都作为中间件存在,可⽤于任何风格的 ASP.NET Core 应⽤程序。在添加 MVC 或Minimal API 中间件之前,您需要确保在应⽤程序中同时注册授权服务和中间件:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.builder.Services

.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer();

builder.Services.AddAuthorization();

builder.Services.AddControllers(); // If MVCvar app = builder.Build();

// Configure the HTTP request pipeline.if (builder.Environment.IsDevelopment()){

app.UseDeveloperExceptionPage();}

app.UseAuthentication();

app.UseAuthorization(); // <-- this needs to come firstapp.MapControllers(); // MVC

app.MapGet(\"/\app.Run();

上⾯的例⼦是使⽤ JWT Bearer 认证。

MVC 和 Minimal API 之间的主要区别在于您声明授权要求的⽅式。

默认安全

如果您对所有端点都有相同的授权要求,我建议您将回退策略设置为要求经过⾝份验证的⽤户:

builder.Services.AddAuthorization(options =>{

options.FallbackPolicy = new AuthorizationPolicyBuilder()

.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser();})

如果您有其他要求或需要允许对特定端点进⾏匿名访问,您可以使⽤以下说明注释您的端点。

MVC

在 MVC 应⽤程序中,使⽤[Authorize]属性装饰您的控制器和/或操作以指定您的授权要求。此属性允许您指定⾓⾊和策略。此⽰例取⾃,将AtLeast21策略应⽤于控制器中定义的所有操作:

[Authorize(Policy = \"AtLeast21\")]

public class AlcoholPurchaseController : Controller{

public IActionResult Index() => Ok();}

如果您的某些 API 端点需要允许匿名访问,您可以使⽤以下[AllowAnonymous]属性装饰这些操作:

[AllowAnonymous][HttpGet(\"/free-for-all\")]

public IActionResult FreeForAll(){

return Ok();}

MinimalAPI

为了使⽤ Minimal API 实现相同的⾏为,我们可以将额外的元数据附加到端点,如下所⽰:

app.MapGet(\"/alcohol\ .RequireAuthorization(\"AtLeast21\");

同样,要允许匿名访问:

app.MapGet(\"/free-for-all\ .AllowAnonymous();

后来我发现[Authorize]在使⽤⽅法组定义处理程序时可以使⽤与 MVC相同的属性:

[Authorize(\"AtLeast21\")]string Alcohol(){ }

总结

Minimal APi提供了⼀种使⽤ ASP.NET Core 构建 API 的替代⽅法。尽管很容易将它们视为“代码较少的 API”,但主要的好处是您拥有⼀个轻量级的基础,您可以在此基础上挑选所需的组件,⽽不是像 MVC 那样沉重的东西,后者可能包含许多出⾊的功能你不使⽤(例如过滤器)。在许多情况下,这可能会导致服务占⽤空间⼩得多,并随后获得性能提升。

值得⼀提的是,过去曾有社区努⼒实现同样的⽬标。在 Web API / OWIN 时代为我们提供了类似的东西,最近为 ASP.NET Core 出现,提供与 Minimal API 类似的功能。

作为 ASP.NET Core 开发⼈员,您现在在如何构建 API ⽅⾯有多种选择,这只能是⼀件好事。如果您希望本⽂涵盖任何其他功能,请在。

因篇幅问题不能全部显示,请点此查看更多更全内容