講一下Asp.net core MVC2.1 裏面的 ApiControllerAttribute
先貼文章鏈接
正文
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不是必須的)。
ApiControllerAttribute
是ControllerAttribute
的子類,所以,框架在處理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.其他
有兩個註意點:
- ApiExplorer 的可見性。 默認所有的controller對
ApiExplorer
都是可見的,所以,不影響swagger 等的生成。 - 只是一個基於特性的路由。集中的路由機制不會應用在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