【译】ASP.NET Core Web APIs(二):使用ASP.NET Core创建Web APIs 【中篇】
原文链接:传送门。
特性路由需求
[ApiConroller]特性使得特性路由成为一个需求。举个例子:
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase
通过由 Startup.Configure中的
UseEndpoints
, UseMvc,或者r UseMvcWithDefaultRoute方法定义的常规路由是不能访问Action方法的。
自动HTTP 400 响应
[ApiController] 特性使得模型验证错误自动触发一个HTTP 400 响应。因此,在一个Action方法中,如下代码是不必要的:
if (!ModelState.IsValid) { return BadRequest(ModelState); }
ASP.NET Core MVC使用ModelStateInvalidFilter 这个动作过滤器来进行上述检查。
默认的BadRequest响应
使用兼容性版本2.1,对于一个HTTP 400响应的默认响应类型是SerializableError。如下的请求体是一个序列化类型的示例:
{ "": [ "A non-empty request body is required." ] }
使用兼容性版本2.2及以后,那么对于一个HTTP 400响应,默认的响应类型是 ValidationProblemDetails。如下的请求体是一个序列化类型的示例:
{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "traceId": "|7fb5e16a-4c8f23bbfc974667.", "errors": { "": [ "A non-empty request body is required." ] } }
这个ValidationProblemDetails
类型:
- 对于在web API中指定错误,提供了一个机器可读的格式。
- 使用 RFC 7807 specification 进行编译。
记录自动的HTTP 400 响应
请参考How to log automatic 400 responses on model validation errors (aspnet/AspNetCore.Docs #12157)。
禁用自动的HTTP 400响应
为了禁用自动HTTP 400行为,可以将属性SuppressModelStateInvalidFilter设置为true。在Startup.ConfigureServices方法中添加如下高亮显示的代码:
services.AddControllers() .ConfigureApiBehaviorOptions(options => { options.SuppressConsumesConstraintForFormFileParameters = true; options.SuppressInferBindingSourcesForParameters = true; options.SuppressModelStateInvalidFilter = true; options.SuppressMapClientErrors = true; options.ClientErrorMapping[404].Link = "https://httpstatuses.com/404"; });
绑定源参数引用
一个绑定源特性定义了一个Action 参数的值被找到的位置。绑定源特性具有如下种类:
特性 | 对应的绑定源 |
---|---|
[FromBody] |
请求体 |
[FromForm] |
请求体中的表单数据 |
[FromHeader] |
请求头 |
[FromQuery] |
请求查询字符串参数 |
[FromRoute] |
来自于当前请求的路由数据 |
[FromServices] |
作为一个Action参数而注入的请求服务 |
原文链接:传送门。
注意:当值可能包含%2f(也就是/)时,请不要使用[FromRouter],%2f不会被转义为 /。如果值可能包含%2f时,请使用[FromQuery]。
如果没有[ApiController] 特性或者像是[FromQuery]这样的绑定源特性,那么ASP.NET Core运行时会试图复杂对象模型绑定器。复杂对象模型绑定i会以定义好的顺序从值提供器中拉取数据。
在如下的示例中,[FromQuery]特性表名discontinuedOnly参数值从请求URL的查询字符串中获取其值:
[HttpGet] public ActionResult<List<Product>> Get( [FromQuery] bool discontinuedOnly = false) { List<Product> products = null; if (discontinuedOnly) { products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList(); } else { products = _productsInMemoryStore; } return products; }
[ApiController]特性为Action参数的默认数据源应用推断规则。这些规则可以节省你的时间,使得你不必通过给Action参数应用绑定特性以手动的指定其绑定源。绑定源推断规则行为如下:
- [FromBody]推断为复杂类型参数。一个[FromBody]推断规则的例外是具有一个特定意义(比如IFormCollection 或者CancellationToken.)的任何复杂类型,内置类型。绑定源推断代码将会忽略那些指定的类型。
- [FromForm]被推断为类型为 IFormFile 和 IFormFileCollection的Action参数。其不会被推断为任何简单类型或者用户自定义类型。
- [FromRouter]被推断为任何Action参数名称匹配于路由模板中的参数的情况。当超过一个的路由匹配到Action参数时,任何一个路由值都会被认为是[FromRouter]。
- [FromQuery]被推断为任何其他的Action参数。
[FromBody]推断注意
[FromBody]不会被推断为任何简单类型,比如string 或者 int。因此,当确实需要那个功能的时候,[FromBody]特性需要被用于简单类型。
当一个Action具有多个方法参数是从请求体中进行绑定,则会抛出一个异常。举个例子,所有如下的Action方法前面会引起一个异常。
- [FromBody]推断在两者上因为其都是复杂类型。
[HttpPost] public IActionResult Action1(Product product, Order order)
- 在一个参数上使用[FromBody],而另一个推断为[FromBody],因为其是复杂类型。
[HttpPost] public IActionResult Action2(Product product, [FromBody] Order order)
- 两者都有[FromBody]特性。
[HttpPost] public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
禁用推断规则
为了禁用绑定源推断,将 SuppressInferBindingSourcesForParameters 属性设置为true。在 Startup.ConfigureServices中添加如下代码:
services.AddControllers() .ConfigureApiBehaviorOptions(options => { options.SuppressConsumesConstraintForFormFileParameters = true; options.SuppressInferBindingSourcesForParameters = true; options.SuppressModelStateInvalidFilter = true; options.SuppressMapClientErrors = true; options.ClientErrorMapping[404].Link = "https://httpstatuses.com/404"; });
Multipart/form-data 请求推断
当一个Action参数被 [FromForm]
特性标记时,[ApiController]特性会应用另一个推断规则。multipart/form-data请求content-type会被推断。
为了禁用默认的行为,在 Startup.ConfigureServices中将 SuppressConsumesConstraintForFormFileParameters属性设置为true:
services.AddControllers() .ConfigureApiBehaviorOptions(options => { options.SuppressConsumesConstraintForFormFileParameters = true; options.SuppressInferBindingSourcesForParameters = true; options.SuppressModelStateInvalidFilter = true; options.SuppressMapClientErrors = true; options.ClientErrorMapping[404].Link = "https://httpstatuses.com/404"; });