ASP.NET Core MVC/WebApi基础系列2(8)

[Route("[Controller]")] [ApiController] public class ModelBindController : Controller { [HttpPost] public IActionResult Post(Employee employee) { //if (!ModelState.IsValid) //{ // return BadRequest(ModelState); //} return Ok(); } }

对比上述代码,我们只是添加ApiController修饰控制器,同时我们已了然内部会自动进行模型验证,所以我们注释了模型验证代码,然后我们也将【FromBody】特性去除,这时我们进行请求,响应如下,符合我们预期:

ASP.NET Core MVC/WebApi基础系列2

我们仅仅只是将添加了ApiController修饰控制器,为何我们将【FromBody】特性去除则请求依然好使,而且结果也如我们预期一样呢?答案则是:参数来源绑定推断,通过ApiController修饰控制器,会用到我们上述提出的第二个约定类(参数绑定信息推断),到了这里是不是发现.NET Core为我们做了好多,别着急,事情还未完全水落石出,接下来我们来看看,我们之前所给出的URL参数绑定到字典上的例子。

[Route("[Controller]")] [ApiController] public class ModelBindController : Controller { [HttpGet] public IActionResult Get(List<Dictionary<string, int>> pairs) { return Ok(); } }

ASP.NET Core MVC/WebApi基础系列2

到这里我们瞬间懵逼了,之前的请求现在却出现了415,也就是媒介类型不支持,我们什么都没干,只是添加了ApiController修饰控制器而已,如此而已,问题出现了一百八十度的大转折,这个问题谁来解释解释下。我们还是看看参数绑定信息约定类的具体实现,一探究竟,如下:

if (!options.SuppressInferBindingSourcesForParameters) { var convention = new InferParameterBindingInfoConvention(modelMetadataProvider) { AllowInferringBindingSourceForCollectionTypesAsFromQuery = options.AllowInferringBindingSourceForCollectionTypesAsFromQuery, }; ActionModelConventions.Add(convention); }

第一个判断则是是否启动参数来源绑定推断,告诉我们这是可配置的,好了,我们将其还原不启用,此时再请求回归如初,如下:

services.Configure<ApiBehaviorOptions>(options=> { options.SuppressInferBindingSourcesForParameters = true; }).AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

那么内置到底做了什么,其实上述答案已经给出了,我们看看上述这行代码:options.AllowInferringBindingSourceForCollectionTypesAsFromQuery,因为针对集合类型,.NET Core无从推断到底是来自于Body还是Query,所以呢,.NET Core再次给定了我们一个可配置选项,我们显式配置通过如下配置集合类型是来自于Query,此时请求则好使,否则将默认是Body,所以出现415。

services.Configure<ApiBehaviorOptions>(options=> { options.AllowInferringBindingSourceForCollectionTypesAsFromQuery = true; }).AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

好了,上述是针对集合类型进行可配置强制指定其来源,那么问题又来了,对于对象又该如何呢?首先我们将上述显式配置集合类型来源于Query给禁用(禁不禁用皆可),我们看看下如下代码:

[Route("[Controller]")] [ApiController] public class ModelBindController : Controller { [HttpGet("GetEmployee")] public IActionResult GetEmployee(Employee employee) { return Ok(); } }

ASP.NET Core MVC/WebApi基础系列2

再次让我们大跌眼镜,好像自从添加上了ApiController修饰控制器,各种问题呈现,我们还是看看.NET Core最终其推断,到底是如何推断的呢?

internal void InferParameterBindingSources(ActionModel action) { for (var i = 0; i < action.Parameters.Count; i++) { var parameter = action.Parameters[i]; var bindingSource = parameter.BindingInfo?.BindingSource; if (bindingSource == null) { bindingSource = InferBindingSourceForParameter(parameter); parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo(); parameter.BindingInfo.BindingSource = bindingSource; } } ...... } // Internal for unit testing. internal BindingSource InferBindingSourceForParameter(ParameterModel parameter) { if (IsComplexTypeParameter(parameter)) { return BindingSource.Body; } if (ParameterExistsInAnyRoute(parameter.Action, parameter.ParameterName)) { return BindingSource.Path; } return BindingSource.Query; } private bool ParameterExistsInAnyRoute(ActionModel action, string parameterName) { foreach (var (route, _, _) in ActionAttributeRouteModel.GetAttributeRoutes(action)) { if (route == null) { continue; } var parsedTemplate = TemplateParser.Parse(route.Template); if (parsedTemplate.GetParameter(parameterName) != null) { return true; } } return false; } private bool IsComplexTypeParameter(ParameterModel parameter) { // No need for information from attributes on the parameter. Just use its type. var metadata = _modelMetadataProvider .GetMetadataForType(parameter.ParameterInfo.ParameterType); if (AllowInferringBindingSourceForCollectionTypesAsFromQuery && metadata.IsCollectionType) { return false; } return metadata.IsComplexType; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdsdzf.html