net core RESTful Api笔记⑤

PUT VS PATCH

put整体更新:资源所有字段都被重写或者设置成默认值。

patch局部更新:使用JsonPatchDocument发送变更的数据,对指定字段进行更新。

修改EmployessController

 [HttpPut("{employeeId}")]
        public async Task<IActionResult> UpdateEmployeeForCompany(Guid companyId, Guid employeeId, EmployUpdateDto employ) 
        {
            if (!await companyRepository.CompanyExistsAsync(companyId)) 
            {
                return NotFound();
            }
            var employeeEntity = await companyRepository.GetEmployeeAsync(companyId,employeeId);
            if (employeeEntity == null) 
            {
                return NotFound();
            }

            //entity转化成updatedto
            //把转化进来的employ的值更新到updateDto
            //把updatedto映射回entity
            mapper.Map(employ, employeeEntity);

            companyRepository.UpdateEmployee(employeeEntity);
            await companyRepository.SaveAsync();

            return NoContent();
        }

添加EmployUpdateDto

using Rountion.API.Eneities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Rountine.API.Models
{
    public class EmployUpdateDto
    {
        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; }
    }
}

结果:

 PUT的更新和新增

正常的put如果传递uri没有找到,返回404,如果客户端允许生成uri,传递到服务器可以创建资源

修改EmployessController

[HttpPut("{employeeId}")]
        public async Task<IActionResult> UpdateEmployeeForCompany(Guid companyId, Guid employeeId, EmployUpdateDto employ) 
        {
            if (!await companyRepository.CompanyExistsAsync(companyId)) 
            {
                return NotFound();
            }
            var employeeEntity = await companyRepository.GetEmployeeAsync(companyId,employeeId);
            if (employeeEntity == null) 
            {
                var employeeToAddEntity = mapper.Map<Employee>(employ);
                employeeToAddEntity.Id = employeeId;

                companyRepository.AddEmployee(companyId,employeeToAddEntity);

                await companyRepository.SaveAsync();

                var dtoToReturn = mapper.Map<EmployeeDto>(employeeToAddEntity);
                return CreatedAtRoute(nameof(GetemployesFromCompany), new { CompanyId = companyId, employeeId = dtoToReturn.Id }, dtoToReturn);
            }

            //entity转化成updatedto
            //把转化进来的employ的值更新到updateDto
            //把updatedto映射回entity
            mapper.Map(employ, employeeEntity);

            companyRepository.UpdateEmployee(employeeEntity);
            await companyRepository.SaveAsync();

            return NoContent();
        }

 Patch用来局部更新,patch请求的body里面的数据格式是json patch:

Patch请求的media type是application/json-patch+json

json patch options:

add:{"op":"add","path":"/biscuit/1","value":{"name":"Ginger Nut"}}

Remove:{"op":"remove","path":"/biscuits"}

Move:{"op":"move","from":"/biscuits","path":"/cookies"}

Replice:{"op":"replice","path":"/biscuits/0/name","value":"Chocolate Digestive"}

Copy:{"op":"copy","from":"/biscuits/0","path":"/best_biscuit"}

Test:{"op":"test","path":"/best_biscuit/name","value":"Chocolate Digestive"}

修改EmployessController

 [HttpPatch("{employeeId}")]
        public async Task<IActionResult> PartiallyUpdateEmployeeForCompany(Guid companyId,Guid employeeId,JsonPatchDocument<EmployUpdateDto> patchDocument) 
        {
            if (!await companyRepository.CompanyExistsAsync(companyId))
            {
                return NotFound();
            }
            var employeeEntity = await companyRepository.GetEmployeeAsync(companyId, employeeId);
            if (employeeEntity == null)
            {
                return NotFound();
            }
            var dotToPatch = mapper.Map<EmployUpdateDto>(employeeEntity);

            //需要处理验证错误

            patchDocument.ApplyTo(dotToPatch);

            mapper.Map(dotToPatch,employeeEntity);

            companyRepository.UpdateEmployee(employeeEntity);

            await companyRepository.SaveAsync();
            return NoContent();
        }
JsonPatchDocument需要在startup里注册
 services.AddControllers(setup =>
            {
                //返回的不是请求类型,报错
                setup.ReturnHttpNotAcceptable = true;
            }).AddNewtonsoftJson(setup=> 
            {
                setup.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            }).AddXmlDataContractSerializerFormatters();

 最后的结果:

 这里其实对返回值和在没有查到情况下没有处理,处理方式:

        [HttpPatch("{employeeId}")]
        public async Task<IActionResult> PartiallyUpdateEmployeeForCompany(Guid companyId,Guid employeeId,JsonPatchDocument<EmployUpdateDto> patchDocument) 
        {
            if (!await companyRepository.CompanyExistsAsync(companyId))
            {
                return NotFound();
            }
            var employeeEntity = await companyRepository.GetEmployeeAsync(companyId, employeeId);
            if (employeeEntity == null)
            {
                var employeeDto = new EmployUpdateDto();
                patchDocument.ApplyTo(employeeDto, ModelState);
                if (TryValidateModel(employeeDto))
                {
                    return ValidationProblem(ModelState);
                }
                var employeeToAdd = mapper.Map<Employee>(employeeDto);
                employeeToAdd.Id = employeeId;

                companyRepository.AddEmployee(companyId, employeeToAdd);
                await companyRepository.SaveAsync();

                var dtoToReturn = mapper.Map<Employee>(employeeToAdd);

                return CreatedAtRoute(nameof(GetemployesFromCompany), new { CompanyId = companyId, employeeId = dtoToReturn.Id }, dtoToReturn);
            }
            var dotToPatch = mapper.Map<EmployUpdateDto>(employeeEntity);

            //需要处理验证错误
            //这个是检测JsonPatchDocument
            //不到对应映射的属性会ModelState出错
            patchDocument.ApplyTo(dotToPatch,ModelState);
            //假如为不空的字段更新""就不行
            if (TryValidateModel(dotToPatch)) 
            {
                return ValidationProblem(ModelState);
            }

            mapper.Map(dotToPatch,employeeEntity);

            companyRepository.UpdateEmployee(employeeEntity);

            await companyRepository.SaveAsync();
            return NoContent();
        }
        /// <summary>
        /// 自定义422
        /// </summary>
        /// <returns></returns>
        public override ActionResult ValidationProblem()
        {
            var options = HttpContext.RequestServices.GetRequiredService<IOptions<ApiBehaviorOptions>>();
            return (ActionResult)options.Value.InvalidModelStateResponseFactory(ControllerContext);
        }

 上面的自定义错误是需要在startup里配置的:AddControllers里

.ConfigureApiBehaviorOptions(
                setup=> {
                    setup.InvalidModelStateResponseFactory = context =>
                    {
                        var problemDetiles = new ValidationProblemDetails(context.ModelState)
                        {
                            Type = "http://ww.baidu.com",
                            Title = "chucuo",
                            Status = StatusCodes.Status422UnprocessableEntity,
                            Detail = "看信息",
                            Instance = context.HttpContext.Request.Path
                        };
                        problemDetiles.Extensions.Add("traceId",context.HttpContext.TraceIdentifier);

                        return new UnprocessableEntityObjectResult(problemDetiles) 
                        {
                            ContentTypes = { "application/problem+json"}   
                        };
                    };
                });

httpDelete:

        [HttpDelete("{employeeId}")]
        public async Task<IActionResult> DeleteEmployeeFromCompany(Guid companyId, Guid employeeId) 
        {
            if (!await companyRepository.CompanyExistsAsync(companyId)) 
            {
                return NotFound();
            }
            var employeeEntity = await companyRepository.GetEmployeeAsync(companyId, employeeId);
            if (employeeEntity == null) 
            {
                return NotFound();
            }
            companyRepository.DeleteEmployee(employeeEntity);
            await companyRepository.SaveAsync();

            return NoContent();
        }

对于删除公司和员工做法一样,不过需要在OnModelCreating的对应关系里标明级联删除DeleteBehavior.Cascade,如果不行就在controller的delete里加上employee加载到内存执行delete。

原文地址:https://www.cnblogs.com/liuyang95/p/13234648.html