1. ASP.NET Core WebAPI学习-1
  2. ASP.NET Core WebAPI学习-2
  3. ASP.NET Core WebAPI学习-3
  4. ASP.NET Core WebAPI学习-4
  5. ASP.NET Core WebAPI学习-5
  6. ASP.NET Core WebAPI学习-6

Web API 增删改方法

ASP.NET Core WebAPI学习-6

PUT vs PATCH

PUT:整体更新/替换
资源所有的字段都被重写了,或者是设置为该字段的默认值
PATCH: 局部更新
使用JsonPatchDocument发送变更的数据,对资源指定的字段进行更新

[HttpPut("{employeeId}")]
public async Task<IActionResult> UpdateEmployeeForCompany(
    Guid companyId,
    Guid employeeId,
    EmployeeUpdateDto employee)
{
    if (!await companyRepository.CompanyExistsAsync(companyId))
    {
        return NotFound();
    }

    var employeeEntity = await companyRepository.GetEmployeeAsync(companyId, employeeId);
    if (employeeEntity == null)
    {
        var employeeToAddEntity = mapper.Map<Employee>(employee);
        employeeToAddEntity.Id = employeeId;
        companyRepository.AddEmployee(companyId, employeeToAddEntity);
        await companyRepository.SaveAsync();
        var dtoToReturn = mapper.Map<EmployeeDto>(employeeToAddEntity);
        return CreatedAtRoute(nameof(GetEmployeeForCompany), new
                              {
                                  companyId,
                                  employeeId = dtoToReturn.Id
                              }, dtoToReturn);
    }

    //entity 转化为updateDto
    //把传进来的employee的值更新到updateDto
    //把updateDto映射回entity
    mapper.Map(employee, employeeEntity);
    companyRepository.UpdateEmployee(employeeEntity);
    await companyRepository.SaveAsync();
    return NoContent(); //204
}

Patch使用方法:

/// <summary>
/// 局部更新Employee
/// </summary>
/// <param name="companyId"></param>
/// <param name="employeeId"></param>
/// <param name="patchDocument"></param>
/// <returns></returns>
[HttpPatch("{employeeId}")]
public async Task<IActionResult> PartiallyUpdateEmployeeForCompany(
    Guid companyId,
    Guid employeeId,
    JsonPatchDocument<EmployeeUpdateDto> patchDocument)
{
    if (!await companyRepository.CompanyExistsAsync(companyId))
    {
        return NotFound();
    }
    var employeeEntity = await companyRepository.GetEmployeeAsync(companyId, employeeId);

    //如果不存在则创建
    if (employeeEntity == null)
    {
        var employeeDto = new EmployeeUpdateDto();
        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<EmployeeDto>(employeeToAdd);
        return CreatedAtRoute(nameof(GetEmployeeForCompany), new
                              {
                                  companyId,
                                  employeeId = dtoToReturn.Id
                              }, dtoToReturn);
    }

    var dtoToPatch = mapper.Map<EmployeeUpdateDto>(employeeEntity);
    //需要处理验证错误
    patchDocument.ApplyTo(dtoToPatch, ModelState);
    if (!TryValidateModel(dtoToPatch))
    {
        return ValidationProblem(ModelState);//400 BadRequest
    }
    mapper.Map(dtoToPatch, employeeEntity);
    companyRepository.UpdateEmployee(employeeEntity);
    await companyRepository.SaveAsync();
    return NoContent();
}

POST vs PUT

POST:

  1. 用来创建资源
  2. 服务器端负责URI的生成
    PUT:
  3. 必须使用一个已知的URI
  4. 如果URI对应的资源不存在,那么应该返回404错误

PUT用来新增或更新
ASP.NET Core WebAPI学习-6

PATCH

  1. 用来做局部更新的
  2. PATCH请求Body里面的数据格式为JSON PATCH (RFC 6902)
  3. PATCH请求的media type是application/json-patch+json
    ASP.NET Core WebAPI学习-6

使用Newtonsoft.JSON替换3.1默认的json库

  1. 安装Microsoft.AspNetCore.Mvc.NewtonsoftJson包
  2. 在Startup.cs的ConfigureServices配置
 public void ConfigureServices(IServiceCollection services)
 {
     services.AddControllers(configure: setup =>
                             {
                                 setup.ReturnHttpNotAcceptable = true;
                             })
         .AddNewtonsoftJson(setup =>
                            {
                                setup.SerializerSettings.ContractResolver =
                                    new CamelCasePropertyNamesContractResolver();
                            })
 }

在Controller里面使用自定义的错误验证报告信息

/// <summary>
/// 使用自定义的错误验证报告信息
/// </summary>
/// <param name="modelStateDictionary"></param>
/// <returns></returns>
public override ActionResult ValidationProblem([ActionResultObjectValue] ModelStateDictionary modelStateDictionary)
{
    var options = HttpContext.RequestServices
        .GetRequiredService<IOptions<ApiBehaviorOptions>>();
    return (ActionResult)options.Value.InvalidModelStateResponseFactory(ControllerContext);
}

HttpDelete删除

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

针对集合资源的分页

集合资源的数量通常比较大,需要对它们进行分页查询
避免性能问题
参数通过Query String进行传递
api/companies?pageNumber=1&pageSize=5
每页的笔数需要进行控制
默认就应该分页
应该对底层的数据存储进行分页

ASP.NET Core WebAPI学习-6
ASP.NET Core WebAPI学习-6
ASP.NET Core WebAPI学习-6
ASP.NET Core WebAPI学习-6

查询分页参数定义:

namespace Routine.Api.ResourceParameters
{
    public class CompanyDtoParameter
    {
        private const int MaxPageSize = 20;
        public string CompanyName { get; set; }
        public string SearchTerm { get; set; }
        public int PageNumber { get; set; } = 1;
        private int pageSize = 5;

        public int PageSize
        {
            get => pageSize;
            set => pageSize = (value > MaxPageSize) ? MaxPageSize : value;
        }
    }
}

PagedList类定义

ResourceUriType:
namespace Routine.Api.Helpers
{
    public enum ResourceUriType
    {
        PreviousePage,
        NextPage
    }
}

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Routine.Api.Helpers
{
    public class PagedList<T> : List<T>
    {
        public int CurrentPage { get; set; }
        public int TotalPages { get; set; }
        public int PageSize { get; set; }
        public int TotalCount { get; set; }
        public bool HasPrevious => CurrentPage > 1;
        public bool HasNext => CurrentPage < TotalPages;
        public PagedList(List<T> items, int count, int pageNumber, int pageSize)
        {
            TotalCount = count;
            PageSize = pageSize;
            CurrentPage = pageNumber;
            TotalPages = (int)Math.Ceiling(count / (double)PageSize);
            AddRange(items);
        }

        public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source, int pageNumber, int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
            return new PagedList<T>(items, count, pageNumber, pageSize);
        }

    }
}

查询定义:

public async Task<PagedList<Company>> GetCompaniesAsync(CompanyDtoParameter parameter)
{
    if (parameter == null)
    {
        throw new ArgumentNullException(nameof(parameter));
    }
    var queryExpression = context.Companies as IQueryable<Company>;
    if (!string.IsNullOrWhiteSpace(parameter.CompanyName))
    {
        queryExpression = queryExpression.Where(x => x.Name == parameter.CompanyName.Trim());
    }
    if (!string.IsNullOrWhiteSpace(parameter.SearchTerm))
    {
        queryExpression = queryExpression.Where(x => x.Name == parameter.SearchTerm.Trim() ||
                                                x.Introduction.Contains(parameter.SearchTerm.Trim()));
    }
    //分页
    //queryExpression = queryExpression
    //    .Skip(parameter.PageSize * (parameter.PageNumber - 1))
    //    .Take(parameter.PageSize);
    //return await queryExpression.ToListAsync();
    return await PagedList<Company>.CreateAsync(queryExpression, parameter.PageNumber, parameter.PageSize);
}

Controller中使用分页:

[HttpGet(Name = nameof(GetCompanies))]
[HttpHead]
public async Task<ActionResult<IEnumerable<CompanyDto>>> GetCompanies([FromQuery]CompanyDtoParameter parameter = null)
{
    //var companies = await companyRepository.GetCompaniesAsync();
    //return Ok(companies);
    //CompanyDtoParameter parameter = null;
    var companies = await companyRepository.GetCompaniesAsync(parameter);
    //var companyDtos = new List<CompanyDto>();
    //foreach (var item in companies)
    //{
    //    companyDtos.Add(new CompanyDto()
    //    {
    //        Id = item.Id,
    //        CompanyName = item.Name
    //    });
    //}
    var previousPageLink = companies.HasPrevious
        ? CreateCompaniesResourceUri(parameter, ResourceUriType.PreviousePage)
        : null;
    var nextPageLink = companies.HasNext
        ? CreateCompaniesResourceUri(parameter, ResourceUriType.NextPage)
        : null;
    var paginationMetadata = new
    {
        totalCount = companies.TotalCount,
        pageSize = companies.PageSize,
        currentPage = companies.CurrentPage,
        totalPages = companies.TotalPages,
        previousPageLink,
        nextPageLink
    };
    //在Response头部,添加分页信息
    Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadata, new JsonSerializerOptions()
                                                                  {
                                                                      Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                                                                  }));

    var companyDtos = mapper.Map<IEnumerable<CompanyDto>>(companies);

    return Ok(companyDtos);
}

/// <summary>
/// 创建分页url
/// </summary>
/// <param name="parameter"></param>
/// <param name="type"></param>
/// <returns></returns>
private string CreateCompaniesResourceUri(CompanyDtoParameter parameter, ResourceUriType type)
{
    switch (type)
    {
        case ResourceUriType.PreviousePage:
            return Url.Link(nameof(GetCompanies), new
                            {
                                pageNumber = parameter.PageNumber - 1,
                                pageSize = parameter.PageSize,
                                companyName = parameter.CompanyName,
                                searchTerm = parameter.SearchTerm
                            });
        case ResourceUriType.NextPage:
            return Url.Link(nameof(GetCompanies), new
                            {
                                pageNumber = parameter.PageNumber + 1,
                                pageSize = parameter.PageSize,
                                companyName = parameter.CompanyName,
                                searchTerm = parameter.SearchTerm
                            });
        default:
            return Url.Link(nameof(GetCompanies), new
                            {
                                pageNumber = parameter.PageNumber,
                                pageSize = parameter.PageSize,
                                companyName = parameter.CompanyName,
                                searchTerm = parameter.SearchTerm
                            });
    }
}

分类:

技术点:

相关文章: