1. 程式人生 > 實用技巧 >net core RESTful Api筆記⑤

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。