最近整理了一下 ASP.NET Core 2.2 Web API 在開發時的注意事項,魔鬼總是出現在細節裡,有些資訊沒遇到問題也不會特別去看,但有時間的時候,從頭到尾釐清一遍,其實還是很有幫助的。
選用正確的基底類別
- ASP.NET Core Web API
- ASP.NET Core MVC
- 請務必繼承自 Controller 類別
- Controller 繼承自 ControllerBase,但額外提供許多 Views 相關功能與輔助方法
- 混合式控制器 (Mixed Controller) (不建議)
ASP.NET Core Web API 常見的 Attributes 說明
- 宣告 Controller/Action 回應的狀態碼
- 宣告 Controller/Action 回應的內容類型 (
Content-Type
)
- 宣告 Controller/Action 可接受的內容類型 (
Accept
)
- 宣告 Action 可接受的 HTTP 動詞與路由範本(route template)
- 宣告 Controller 屬性路由的預設前置詞 (Route Prefix)
- 宣告 Controller 啟用常見的 API 特性 (API-specific behaviors)
- 套用
[ApiController]
屬性之後,將會啟用以下特性:
-
必須使用屬性路由(Attribute Routing)
-
只要發生模型驗證失敗,就會自動回應 HTTP 400 (Bad Request),並且以 ValidationProblemDetails 型別回應,預設的 JSON 結構如下:
{
"errors": {
"name": [
"The name field is required."
]
},
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "80000093-0002-fe00-b63f-84710c7967bb"
}
這也意味著以下程式碼可以完全從每一個 Action 中移除:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
你可以關閉驗證失敗時自動 HTTP 400 回應
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
// 關閉驗證失敗時自動 HTTP 400 回應
options.SuppressModelStateInvalidFilter = true;
});
也可以自訂驗證錯誤時的回應訊息
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});
如果你想在 InvalidModelStateResponseFactory
工廠方法中寫些 Log 訊息,可以參考 How to log automatic 400 responses on model validation errors 這篇討論中提供的範例程式。
-
繫結 Action 參數時會自動套用模型繫結的預設規則
- 複雜型別預設就會自動套用
[FromBody]
屬性,但有些特殊目的的型別是例外,例如:IFormCollection and CancellationToken
- 參數型別如果是 IFormFile 或 IFormFileCollection 的話,會自動套用
[FromForm]
屬性
- 任何參數名稱如果剛好等於路由範本(route template)的路由參數名稱,就會自動套用
[FromRoute]
屬性
- 簡單模型或任何其他參數,全部都會自動套用
[FromQuery]
屬性
- 任何預設規則外的模型繫結,都需要明確指定以下任何一個屬性:
[FromBody]
- 從 Request body 取值
[FromForm]
- 從 Request body 中的表單資料取值
[FromHeader]
- 從 Request header 取值
[FromQuery]
- 從 Query String 取值
[FromRoute]
- 從目前要求的路由變數取值
[FromServices]
- 從 DI 容器中取得服務物件
-
當設定為 CompatibilityVersion.Version_2_2
相容模式後,你可以在組件層級套用 [ApiController]
屬性,如下範例:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
[assembly: ApiController]
namespace WebApiSample
{
public class Startup
{
...
}
}
全新的 HTTP 錯誤回應模型
如果你將 MVC 相容層級設定為 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
的話,預設所有的錯誤都會遵照 RFC 7807 specification 的規定進行回應,從 ASP.NET Core 2.1 開始建立了一個 ProblemDetails 模型類別,專門用來描述各種不同的 HTTP API 錯誤資訊。
從 ASP.NET Core 2.2 開始,ASP.NET Core 才正式將所有錯誤回應改用 ProblemDetails 模型類別輸出!如果你想維持以前的預設訊息格式 (HttpError),可以將 相容模式 調整為 CompatibilityVersion.Version_2_1
,如下範例:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
當你的 Action 程式這樣寫:
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return NotFound();
}
當回應 HTTP 404 的時候,其 Response Body 內容如下:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"title": "Not Found",
"status": 404,
"traceId": "8000001c-0000-f900-b63f-84710c7967bb"
}
這個問題回應的預設訊息內容是可以自定的,你可以透過調整 ClientErrorMapping
屬性來改變這個內容,請參考以下 ConfigureServices()
中的設定:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressConsumesConstraintForFormFileParameters = false;
options.SuppressInferBindingSourcesForParameters = false;
options.SuppressModelStateInvalidFilter = false;
// 設定 Client Errors (4xx) 的 Title 與 Link 內容
options.SuppressMapClientErrors = false;
options.ClientErrorMapping[404].Title = "找不到網頁";
options.ClientErrorMapping[404].Link = "https://httpstatuses.com/404";
});
設定完後,其回應內容如下:
{
"type": "https://httpstatuses.com/404",
"title": "找不到網頁",
"status": 404,
"traceId": "80000044-0001-fb00-b63f-84710c7967bb"
}
如果你想要完全關閉用戶端錯誤(Client Errors)的任何訊息,則可以做出以下調整:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressMapClientErrors = true;
});
相關連結