1. 程式人生 > >講一下Asp.net core MVC2.1 裏面的 ApiControllerAttribute

講一下Asp.net core MVC2.1 裏面的 ApiControllerAttribute

github contex 子類 手動 formatter 分組 type aspnet new

先貼文章鏈接

正文

ASP.NET Core MVC 2.1 特意為構建 HTTP API 提供了一些小特性,今天主角就是 ApiControllerAttribute. (註:文章是18年2月份的,所以文章提到了core2.1還沒發布)。

0. ApiControllerAttribute 繼承自 ControllerAttribute

ASP.NET Core MVC 已經有了ControllerAttribute,這個用來標註一個類型是否是Controller。標註了之後框架就知道哪些是系統裏面的Controller了。(框架也有其他方法來獲取程序裏面的Controller,所以,這個ControllerAttribute不是必須的)。

ApiControllerAttributeControllerAttribute的子類,所以,框架在處理Controller發現的時候和ControllerAttribute標註的對象是一樣的。

但是,因為ApiControllerAttribute 實現了IApiBehaviorMetadata接口,所以提供了一些額外的特這些特性是以HTTP Api為出發點的。下面介紹一下這些特性。

1. 自動模型狀態驗證

這個是重點,框架會幫你自動驗證model的state,也就是ModelState.(註:不過我就是因為用FluentValidation的時候模型驗證不管用了出問題了才找到這篇文章的).

框架會為你自動註冊ModelStateInvalidFilter,這個會運行在OnActionExecuting事件裏面(具體來說:在action執行之前,model綁定之後)。他內部會檢查ModelState是否為Valid,如果為InValid會直接返回400 BadRequest,這樣就沒有必要執行後面的代碼,提高效率。

它會自動把model state 放到response裏面,content type 是application/problem+json。當然你也可以自定義,因為畢竟你會有自己的驗證,後文會講。

下面,我們先來舉個例子說一下。

  • 之前的寫法
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook([FromBody]Book book)
    {
        if (ModelState.IsValid) //判斷狀態
        {
            return BadRequest(ModelState);
        }
        //其他代碼。。。
    }
}
  • 現在可以這麽寫
[ApiController]
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook(Book book)
    {
        //直接寫,不用驗證modelstate
    }
}

順道說一下,ModelStateInvalidFilter是個公共類,所以,不用ApiControllerAttribute也可以使用它。

2.參數綁定策略的自動推斷

另一個非常有用的特性是action裏面的參數的模型綁定可以自動推斷。

ASP.NET Core MVC裏面有一個比較令人惱怒的問題你需要手動給參數指定[FromBody]這個特性,以便讓系統知道如何從Request body裏面反序列化他們,比如反序列化json。因此,寫了很多第三方的庫來解決這個問題,比如:

  • WebApiContrib.Core.Formatter.Bson
  • WebApiContrib.Core.Formatter.Csv
  • 其他不寫了,,就舉個例子

現在,這些可以自動解決了。

除此之外,如果一個參數在route裏面定義了,他會自動從先從path,也就是url上嘗試綁定,不行的話會去從查詢參數上綁定。IFormFlie默認從form表單上綁定獲取。

下面看代碼:

  • 之前
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook([FromBody]Book book)
    {
        // 寫代碼
    }
}
  • 現在
[ApiController]
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook(Book book)//FromBody沒必要寫了
    {
        // 寫代碼
    }
}

3. 處理multipart/form-data請求

如果你的action裏面的一個參數指定了[FromFile]特性(這通常是用於文件上傳的),框架會自動假設請求是multipart/form-data。這個是用來解決社區裏面提的這個問題。

不過這個也是可選的,只要你自己定義在action上定義一下[Consumes(...)]

4.其他

有兩個註意點:

  1. ApiExplorer 的可見性。 默認所有的controller對ApiExplorer都是可見的,所以,不影響swagger 等的生成。
  2. 只是一個基於特性的路由。集中的路由機制不會應用在API controller,框架要求只能使用基於特性的路由,即在action上指定[Route("XXX")]的方式。

5. 行為自定義

像MVC框架的大部分組件一樣,ApiControllerAttribute的行為是高度可自定義的。首先,上面說的大部分內容都是可以簡單的用 on/off 來切換。

具體的設置是在startup方法裏面通過ApiBehaviorOptions來實現,先來看一下這個類。

    public class ApiBehaviorOptions
    {
        public Func<ActionContext, IActionResult> InvalidModelStateResponseFactory { get; set; }

        public bool SuppressModelStateInvalidFilter { get; set; }

        public bool SuppressInferBindingSourcesForParameters { get; set; }

        public bool SuppressConsumesConstraintForFormFileParameters { get; set; }
    }

所有bool類型的屬性默認都是false。Suppres有阻止的意思。可以通過以下方法進行設置。

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
    options.SuppressConsumesConstraintForFormFileParameters = true;
});

來看一下InvalidModelStateResponseFactory屬性,他是一個返回IActionResult的Func,通過他,我們可以註入自己的委托來實現需要的返回類型,舉個例子。

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContext => 
    {
        var errors = actionContext.ModelState
            .Where(e => e.Value.Errors.Count > 0)
            .Select(e => new Error
            {
            Name = e.Key,
            Message = e.Value.Errors.First().ErrorMessage
            }).ToArray();
 
        return new BadRequestObjectResult(errors);
    }
});
 
class Error
{
    public string Name { get; set; }
 
    public string Message { get; set; }
}

講一下Asp.net core MVC2.1 裏面的 ApiControllerAttribute