本文内容来自书籍: Marinko Spasojevic - Ultimate ASP.NET Core Web API - From Zero To Six-Figure Backend Developer (2nd edition)
[HttpGet("{id:guid}", Name = "CompanyById")]
,将GET修改后,给这个action
起了个名字
我们之前有输出的DTO,现在需要一个输入的DTO
public record CompanyForCreationDto(string Name, string Address, string Country);
注意:在一些项目中,输入和输出的DTO是同一个,但是推荐将它们分开,这样更加灵活容易重构
在输出的时候,我们没有验证参数,但是现在有了输入,需要验证输入的每个参数
[HttpPost] public IActionResult CreateCompany([FromBody] CompanyForCreationDto? company) { if (company is null) return BadRequest("CompanyForCreationDto object is null"); var createdCompany = _service.CompanyService.CreateCompany(company); return CreatedAtRoute("CompanyById", new { id = createdCompany.Id }, createdCompany); }
因为输入是来自客户端,所以有可能是null,所以需要验证入参
然后就是在新增成功之后的返回CreatedAtRoute
,这个方法会给客户端返回201
,代表以创建
而且,会在响应头的Location
属性中,填充查找这个新增的company
的地址
所以,需要提供获得这个company
的action
,也就是前面GET方法起了个名字的原因
[ApiController]
属性背后做了几件事
当POST请求没有请求体,就会自动返回400,这是一种很好的行为,不过它阻止了我们返回自定义的不同信息和错误码给客户端
如果需要开启自定义的响应,需要在主项目中配置,压制/禁止默认的模型状态验证,也就是[ApiController]
所使用的验证
builder.Services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });
这样,我们就可以不需要将[ApiController]
删除来实现返回自定义的响应,因为属性还提供了其他的我们想要的功能
在一些参数传进来的时候,可能由于参数是自定义类型的数据,所以需要我们自定义一些数据绑定器
public class ArrayModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (!bindingContext.ModelMetadata.IsEnumerableType) { bindingContext.Result = ModelBindingResult.Failed(); return Task.CompletedTask; } var providedValue = bindingContext.ValueProvider .GetValue(bindingContext.ModelName) .ToString(); if (string.IsNullOrEmpty(providedValue)) { bindingContext.Result = ModelBindingResult.Success(null); return Task.CompletedTask; } var genericType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0]; var converter = TypeDescriptor.GetConverter(genericType); var objectArray = providedValue .Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) .Select(x => converter.ConvertFromString(x.Trim())) .ToArray(); var guidArray = Array.CreateInstance(genericType, objectArray.Length); objectArray.CopyTo(guidArray, 0); bindingContext.Model = guidArray; bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); return Task.CompletedTask; } }
IEnumerable
类型的,所以检查我们需要绑定的参数IEnumerable
的元素类型,然后根据这个获取到类型转换器,这个转换器的终点也就是IEnumerable
的元素类型然后将这个绑定器使用在控制器上
public IActionResult GetCompanyCollection([ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable<Guid> ids)