.net core RESTful Api笔记③
HTTP安全性和幂等性
安全性指的是方法执行后并并不会改变资源的表述
幂等性指的是方法无论执行多少次都会得到同样的结果
这里创建Company资源:
[HttpPost] public async Task<ActionResult<CompanyDto>> CreateCompany([FromBody]CompanyAddDto company) { var entity = _mapper.Map<Company>(company); _companyRepository.AddCompany(entity); await _companyRepository.SaveAsync(); var returnDto = _mapper.Map<CompanyDto>(entity); return CreatedAtRoute(nameof(GetCompanies),new { companyId= returnDto.Id},returnDto); }
里面的CreatedAtRoute返回符合resultful风格,并且需要新的添加dto,映射的addmap也要添加。
为了保证重构方便,而且读取的dto资源不够
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.Models { public class CompanyAddDto { public string Name { get; set; } public string introduction { get; set; } } }
添加employee资源:
也是和上面比较类似
[HttpPost] public async Task<ActionResult<EmployeeDto>> CreaterEmployeeForCompany(Guid companyId,EmployeeAddDto employee) { if (!await this.companyRepository.CompanyExistsAsync(companyId)) { return NotFound(); } var entity = mapper.Map<Employee>(employee); companyRepository.AddEmployee(companyId,entity); await companyRepository.SaveAsync(); var dtoToReturn = mapper.Map<EmployeeDto>(entity); return CreatedAtRoute(nameof(GetemployesFromCompany),new {CompanyId= companyId, employeeId= dtoToReturn.Id }, dtoToReturn); }
AddDto:
using Rountion.API.Eneities; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.Models { public class EmployeeAddDto { public string EmployeeNo { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Gender Gender { get; set; } public DateTime DateOfBirth { get; set; } } }
同时创建父子资源:
修改CompanyAddDto:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.Models { public class CompanyAddDto { public string Name { get; set; } public string introduction { get; set; } public ICollection<EmployeeAddDto> Employee { get; set; } = new List<EmployeeAddDto>(); } }
结果:
创建多个资源:
controller:
using AutoMapper; using Microsoft.AspNetCore.Mvc; using Rountine.API.Models; using Rountine.API.Services; using Rountion.API.Eneities; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.Controllers { [ApiController] [Route("api/companycollections")] public class CompanyCollectionsController:ControllerBase { private readonly IMapper _mapper; private readonly ICompanyRepository _companyRepository; public CompanyCollectionsController(ICompanyRepository companyRepository, IMapper mapper) { _mapper = mapper??throw new ArgumentException(nameof(mapper)); _companyRepository = companyRepository?? throw new ArgumentException(nameof(companyRepository)); } [HttpPost] public async Task<ActionResult<IEnumerable<CompanyDto>>> CreateCompanyCollections(IEnumerable<CompanyAddDto> companies) { var companyEntities = _mapper.Map<IEnumerable<Company>>(companies); foreach (var company in companyEntities) { _companyRepository.AddCompany(company); } await _companyRepository.SaveAsync(); return Ok(); } } }
这里返回时200,正常的应该返回201,所以这里还需要修改下
修改方式也比较麻烦:
controller:里面修改创建并添加了查询保证201状态码和返回添加的内容
using AutoMapper; using Microsoft.AspNetCore.Mvc; using Rountine.API.Helpers; using Rountine.API.Models; using Rountine.API.Services; using Rountion.API.Eneities; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.Controllers { [ApiController] [Route("api/companycollections")] public class CompanyCollectionsController : ControllerBase { private readonly IMapper _mapper; private readonly ICompanyRepository _companyRepository; public CompanyCollectionsController(ICompanyRepository companyRepository, IMapper mapper) { _mapper = mapper ?? throw new ArgumentException(nameof(mapper)); _companyRepository = companyRepository ?? throw new ArgumentException(nameof(companyRepository)); } [HttpGet("{ids}",Name=nameof(GetCompanyCollection))] public async Task<IActionResult> GetCompanyCollection( [FromRoute] [ModelBinder(BinderType=typeof(ArrayModelBinder))]IEnumerable<Guid> ids) { if (ids == null) { return BadRequest(); } var entities = await _companyRepository.GetCompaniesAsync(ids); if (ids.Count() != entities.Count()) { return NotFound(); } var dtotoreturn = _mapper.Map<IEnumerable<CompanyDto>>(entities); return Ok(dtotoreturn); } [HttpPost] public async Task<ActionResult<IEnumerable<CompanyDto>>> CreateCompanyCollections(IEnumerable<CompanyAddDto> companies) { var companyEntities = _mapper.Map<IEnumerable<Company>>(companies); foreach (var company in companyEntities) { _companyRepository.AddCompany(company); } await _companyRepository.SaveAsync(); var dtosToReturn = _mapper.Map<IEnumerable<CompanyDto>>(companyEntities); var idsString = string.Join(",",dtosToReturn.Select(x=>x.Id)); return CreatedAtRoute(nameof(GetCompanyCollection),new { ids = idsString }, dtosToReturn); } } }
这里需要将传入的ids转化成IEnumerable<Guid>所以需要自定义一个model类型将传入的转化成guid
ArrayModelBinder:
using Microsoft.AspNetCore.Mvc.ModelBinding; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Threading.Tasks; namespace Rountine.API.Helpers { public class ArrayModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (!bindingContext.ModelMetadata.IsEnumerableType) { bindingContext.Result = ModelBindingResult.Failed(); return Task.CompletedTask; } var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ToString(); if (string.IsNullOrEmpty(value)) { bindingContext.Result = ModelBindingResult.Success(null); return Task.CompletedTask; } var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0]; var converter = TypeDescriptor.GetConverter(elementType); var values = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(x=>converter.ConvertFromString(x.Trim())).ToArray() ; var typedValues = Array.CreateInstance(elementType,values.Length); values.CopyTo(typedValues,0); bindingContext.Model = typedValues; bindingContext.Result = ModelBindingResult.Success(bindingContext.Model); return Task.CompletedTask; } } }
请求: