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

  • 2021 年 10 月 18 日
  • 筆記

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

//benfoster.io/blog/mvc-to-minimal-apis-aspnet-6/

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 的一些新特性,使事情變得更加「最小化」。

引導

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 中間件。

最少的API

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("/", () => "Hello World!");

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", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };
    
    [HttpGet]
    public IEnumerable<WeatherForecast> 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<WeatherForecast> GetByLocation(string location)
{

}

請求時,/weatherforecast/locations/london該值london將綁定到相應的操作參數。

與它們的 Minimal API 對應物相比,MVC 控制器看起來非常臃腫。但是,值得注意的是,控制器也可以是 POCO(Plain Old CLR Objects)。為了獲得與上面的「Hello World」最小 API 示例相同的結果,我們只需要:

public class RootController
{
    [HttpGet("/")]
    public string Hello() => "Hello World";
}

從這裡你可以看到尤其是當你考慮到你仍然需要一定程度的模組化時,即使使用最小的 API, MVC 也可以是「最小的」,。

最少的API

要使用 Minimal API 定義路由和處理程式,請使用Map(Get|Post|Put|Delete)方法。有趣的是沒有MapPatch方法,但您可以使用MapMethods.

要使用 Minimal API 實現相同的天氣預報示例:

var summaries = new[]{    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"};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}", (string 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){    }

最少的API

Minimal API 中的模型綁定非常相似;您使用您希望從請求中綁定的類型配置您的處理程式委託。複雜類型將從請求正文中自動綁定,而簡單類型將從路由或查詢字元串參數中綁定。使用 Minimal API 實現的相同示例如下:

app.MapPost("/payments", (PaymentRequest paymentRequest) => {    });app.MapGet("/portfolios/{id}", (int id, int? page, int? pageSize) => {});

為了指定默認值,您需要傳遞一個方法作為委託,因為 C# 尚不支援內聯 lambda 函數的默認值:

app.MapGet("/search/{id}", Search);app.Run();IResult Search(int id, int? page = 1, int? pageSize = 10){}

[FromQuery]屬性不支援綁定複雜類型。有可用於自定義模型綁定的擴展點,我將在後面的文章中介紹。

要支援可選的請求參數,您可以應用與[FromBody]MVC相同的屬性,指定EmptyBodyBehavior

app.MapPost("/payments", ([FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]PaymentRequest? paymentRequest]) => {});

HTTP 響應

MVC 和 Minimal API 都會自動將您的返回類型序列化到響應正文並返回 HTTP 200 (OK) 響應,例如:

// MVC[HttpPost("/echo")]public EchoRequest Echo(EchoRequest echo) => echo;// Minimal APIapp.MapPost("/echo", (EchoRequest echo) => echo);

您還可以返回voidTask返回一個空的 HTTP 200 (OK) 響應:

// MVC[HttpPost("/echo")]public void Echo(EchoRequest echo) => {};// Minimal APIapp.MapPost("/echo", (EchoRequest echo) => {});

除了隱式轉換之外,MVC 和 Minimal API 都有一個豐富的 HTTP 響應對象模型,涵蓋了最常見的 HTTP 響應。

MVC

在 MVC 中,您可以返回IActionResult並使用許多內置實現,例如AcceptedResult. 如果您是從ControllerBase那裡派生控制器的,那麼大多數響應類型都可以使用輔助方法:

[HttpDelete("/projects/{id}")]public IActionResult Delete(int id){    return Accepted();}

最少的API

使用 Minimal API,我們可以返回IResult. 在Results靜態類可以很容易地產生了一些內置的響應類型:

app.MapDelete("/projects/{id}", (int 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<IActionResult> Delete(string id)    {        await _cache.Delete(id);        return Accepted();    }}

依賴項在啟動時註冊(現在默認在 Program.cs 中):

builder.Services.AddScoped<ICache, MemoryCache>();

使用範圍生命周期註冊的服務將在 MVC 應用程式中按 HTTP 請求創建。

最少的API

使用 Minimal API,我們仍然可以從依賴注入中受益,但不是使用構造函數注入,而是在處理程式委託中將依賴作為參數傳遞:

app.MapDelete("/cache/{id}", async (string id, ICache cache) =>{    await cache.Delete(id);    return Results.Accepted();});

這種方法更純粹,可以使測試更容易。不利的一面是,一旦您獲得多個依賴項,您的處理程式定義就會變得非常嘈雜。

最後,雖然依賴在 內本地聲明的依賴項可能很誘人Program.cs,但這不僅會使測試變得困難,而且還會導致範圍問題。我建議儘可能利用 DI 容器,即使是單例依賴。

語境

您的 API 可能需要訪問有關 HTTP 請求的其他資訊,例如當前用戶的標頭或詳細資訊。MVC 和 Minimal API 都構建在您熟悉的相同 ASP.NET Core HTTP 抽象之上。

MVC

在MVC中,獲得您的控制器時,從ControllerBase您可以訪問HttpContextHttpRequestHttpResponse和當前用戶(ClaimsPrincipal從基類屬性):

[HttpGet]public IEnumerable<WeatherForecast> Get(){    if (Request.Headers.TryGetValue("some header", out var headerValue))    {    }    bool isSpecialUser = User.Identity.IsAuthenticated         && User.HasClaim("special");

如果您的控制器是一個簡單的 POCO 並且不是派生自ControllerBase您,則需要使用構造函數注入來注入IHttpContextAccessor您的控制器或直接訪問請求、響應和用戶,請為這些類型執行一些 DI 連接。如果 POCO 控制器可以利用類似於下面描述的 Minimal API 的方法注入,那就太好了。

最少的API

使用 Minimal API,您可以通過將以下類型之一作為參數傳遞給處理程式委託來訪問相同的上下文資訊:

  • HttpContext
  • HttpRequest
  • HttpResponse
  • ClaimsPrincipal
  • CancellationToken (請求中止)
app.MapGet("/hello", (ClaimsPrincipal user) => {    return "Hello " + user.FindFirstValue("sub");});

鏈接生成

在某些情況下,您需要生成指向 API 其他部分的鏈接。在 ASP.NET Core 中,我們可以依靠現有的 HTTP 和路由基礎結構來避免對 URI 組件進行硬編碼。要生成到已知路線的鏈接,我們首先需要一種方法來識別它們。

MVC

在 MVC 中,我們可以將一個Name屬性傳遞給我們用來裝飾控制器操作的路由屬性,例如:

[HttpGet("products/{id}", Name = "get_product")]public IActionResult GetProduct(int id){}

然後我們可以使用IUrlHelper生成指向該路由的鏈接:

[HttpPost("products", Name = "create_product")]public IActionResult CreateProduct(CreateProduct command){    var product = Create(command);    return Created(Url.Link("get_product", new { id = product.Id }));}

請注意路由的路由參數(get_product在本例中為 ID)是如何作為匿名對象傳遞的。

IUrlHelper可通過Url酒店獲得ControllerBase。或者,您可以將它注入到您的類中,前提是您HTTP 範圍內。

最少的API

使用 Minimal API,您可以通過附加元數據來命名端點:

app.MapGet("/products/{id}", (int id) =>{    return Results.Ok();}).WithMetadata(new EndpointNameMetadata("get_product"));

上述內容的簡寫版本WithName將在未來版本中提供。

還有一個出色的建議是在傳遞方法組而不是內聯 lambda 時隱式生成端點名稱。從上面的問題:

// These endpoints have their name set automaticallyapp.MapGet("/todos/{id}", GetTodoById);async Task<IResult> 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", async (HttpContext httpContext, IMediator mediator, LinkGenerator links, PaymentRequest payment) =>{    var result = await mediator.Send(payment);    return result.Match(        invalidRequest => invalidRequest.ToValidationProblem(),        success => Results.Created(links.GetUriByName(httpContext, "get_payment", new { id = success.Id})!, payment)    );})

一些內置的 Result 助手代表你處理這個樣板。同樣的例子,簡化為Results.CreatedAtRoute

app.MapPost("payments", async (HttpContext httpContext, IMediator mediator, PaymentRequest payment) =>{    var result = await mediator.Send(payment);    return result.Match(        invalidRequest => invalidRequest.ToValidationProblem(),        success => Results.CreatedAtRoute("get_payment", new { id = success.Id }, success);    );})

驗證

MVC

輸入驗證是任何 API 的重要組成部分。MVC 在 ASP.NET 之上添加的功能之一是模型狀態。從文檔

模型狀態表示來自兩個子系統的錯誤:模型綁定和模型驗證。源自模型綁定的錯誤通常是數據轉換錯誤。

MVC 還包括對通過屬性進行驗證的內置支援,例如:

public class PaymentRequest{    [Required]    public int? Amount { get; set; }        [Required]    [StringLength(3)]    public string Currency { get; set; }}

提示:一個流行的選擇是為Fluent Validation替換基於默認屬性的驗證。

綁定到此模型類型時,任何驗證錯誤都會自動添加到模型狀態。在控制器中,我們可以檢查它並採取適當的措施:

public IActionResult Post(PaymentRequest paymentRequest){    if (!ModelState.IsValid)    {        // return validation error    }    // otherwise process}

事實上,如果我們用[ApiController]約定來裝飾我們的控制器,我們甚至不需要做上面的事情。這將過濾器應用於 MVC 管道,該過濾器將驗證任何請求的輸入並在必要時返回問題詳細資訊響應。

{    "type": "//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 中間件中沒有的其他上下文。這是允許內置驗證中間件自動執行的原因,因為它能夠模型綁定發生運行。

最少的API

就目前而言,Minimal API 沒有任何內置的驗證支援。但是,您當然可以自由地推出自己的產品。

Damian Edwards 創建了MinimalValidation,這是一個利用類似於默認 MVC 驗證的驗證屬性的小型庫:

app.MapPost("/widgets", (Widget widget) =>    !MinimalValidation.TryValidate(widget, out var errors)        ? Results.BadRequest(errors)        : Results.Created($"/widgets/{widget.Name}", widget));app.Run();class Widget{    [Required, MinLength(3)]    public string? Name { get; set; }    public override string? ToString() => Name;}

您可以在此處找到更多示例。

我個人更喜歡使用Fluent Validation通常用這個庫替換 MVC 中基於屬性的驗證。

下面是使用 Fluent Validation 和最少 API 的示例:

builder.Services.AddValidatorsFromAssemblyContaining<PaymentRequest>(lifetime: ServiceLifetime.Scoped);var app = builder.Build();app.MapPost("payments", async (IValidator<PaymentRequest> validator, PaymentRequest paymentRequest) =>{    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<PaymentRequest>    {        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<string, string[]> 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<T>顯式註冊實現。無論哪種方式,這都意味著我的驗證器可以提供給我的處理程式,我可以驗證傳入的類型。

這裡的一個缺點是您可能最終會在每個處理程式中編寫相同的樣板驗證檢查。可以通過一些重構來減少它,但是沒有可以訪問綁定模型的預處理程式鉤子,我們不能像使用 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());

注意:開箱即用仍不支援蛇形命名法(Snake casing,譯者按:當今許多程式語言都建議在某些情況下使用類似蛇的命名法,對於單個字元或單詞(例如A,PYTHON,BOY),當將它們用作變數名時,大致所有小寫字母,全部大寫字母和首字母大寫字母。但是,程式語言通常需要使用多個單詞或符號來表示變數名中更豐富的含義。 英語習慣於使用空格分隔單詞,但是這種用法會給程式語言帶來麻煩,因此程式設計師創建了其他方法,蛇形命名法就是用下劃線分隔兩個字元,使其可讀性更強)。您可以在此處找到上述策略的源程式碼。

最少的 API

最小的 API 依賴於許多擴展方法來序列化到/從 JSON。它們允許JsonSerializerOptions提供,但否則會退回到JsonOptionsHttContext.Request.Services. 您可以在啟動時配置這些選項:

builder.Services.Configure<JsonOptions>(opt =>{    opt.SerializerOptions.PropertyNamingPolicy = new SnakeCaseNamingPolicy());});

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

我在深入研究源程式碼時發現的一件事是,序列化對象ObjectResultIResult實現的基類僅支援序列化 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(); // MVCapp.MapGet("/", () => "Hello World!"); // Minimal APIsapp.Run();

上面的例子是使用 JWT Bearer 認證。

MVC 和 Minimal API 之間的主要區別在於您聲明授權要求的方式。

默認安全

如果您對所有端點都有相同的授權要求,我建議您將回退策略設置為要求經過身份驗證的用戶:

builder.Services.AddAuthorization(options =>{    options.FallbackPolicy = new AuthorizationPolicyBuilder()      .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)      .RequireAuthenticatedUser();})

如果您有其他要求或需要允許對特定端點進行匿名訪問,您可以使用以下說明注釋您的端點。

MVC

在 MVC 應用程式中,使用[Authorize]屬性裝飾您的控制器和/或操作以指定您的授權要求。此屬性允許您指定角色和策略。此示例取自Microsoft Docs,將AtLeast21策略應用於控制器中定義的所有操作:

[Authorize(Policy = "AtLeast21")]public class AlcoholPurchaseController : Controller{    public IActionResult Index() => Ok();}

如果您的某些 API 端點需要允許匿名訪問,您可以使用以下[AllowAnonymous]屬性裝飾這些操作:

[AllowAnonymous][HttpGet("/free-for-all")]public IActionResult FreeForAll(){    return Ok();}

最少的API

為了使用 Minimal API 實現相同的行為,我們可以將額外的元數據附加到端點,如下所示:

app.MapGet("/alcohol", () => Results.Ok())    .RequireAuthorization("AtLeast21");

同樣,要允許匿名訪問:

app.MapGet("/free-for-all", () => Results.Ok())    .AllowAnonymous();

後來我發現[Authorize]在使用方法組定義處理程式時可以使用與 MVC相同的屬性:

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

包起來

最小 API 提供了一種使用 ASP.NET Core 構建 API 的替代方法。儘管很容易將它們視為「程式碼較少的 API」,但主要的好處是您擁有一個輕量級的基礎,您可以在此基礎上挑選所需的組件,而不是像 MVC 那樣沉重的東西,後者可能包含許多出色的功能你不使用(例如過濾器)。在許多情況下,這可能會導致服務佔用空間小得多,並隨後獲得性能提升。

值得一提的是,過去曾有社區努力實現同樣的目標。Nancy在 Web API / OWIN 時代為我們提供了類似的東西,最近Carter為 ASP.NET Core 出現,提供與 Minimal API 類似的功能。

作為 ASP.NET Core 開發人員,您現在在如何構建 API 方面有多種選擇,這隻能是一件好事。如果您希望本文涵蓋任何其他功能,請在Twitter 上聯繫