使用ASP.NET Core 3.x 构建 RESTful API P21 P22 创建资源 创建子资源

使用ASP.NET Core 3.x 构建 RESTful API P21 P22 创建资源 创建子资源


博客园文章Id:12678771


创建父资源

根据经验,我们在做查询,创建,更新时,我们应该使用不同的Dto类,虽然绝大多数时候,可能他们的成员是一样的,但是业务是变化的,创建不同的Dto类的好处,在于可以快速的方便我们对于某一块业务进行重构.

下面我们开始编写用于创建公司资源的API

[HttpPost]
public async Task<ActionResult<CompanyDto>> CreateCompany([FromBody]CompanyAddDto company)
{
            //当请求的Body转换成CompanyAddDto失败时,company就会为null,(在.Net Core 3.x之前,
            //因为早期版本是没有ApiController这个特性的)所以我们在此处应该判断一下,
            //但是在.Net Core 3.x (添加[ApiController特性])之后,当请求的Body转换失败之后,.Net Core 3.x 会自动返回 400 状态码,所以下面的if判断可以不写.
            if (company == null)
            {
                return BadRequest();
            }

            var entity = this._mapper.Map<Company>(company);

            this._companyRepository.AddCompany(entity);

            await this._companyRepository.SaveAsync();

            //POST请求成功之后之后,我们应该返回201状态码

            var returnDto = this._mapper.Map<CompanyDto>(entity);

            //返回插入的资源
            //第一个参数:返回可找到当前资源的URI
            //第二个参数:路由的值
            //第三个参数:返回的是响应的Body
            return CreatedAtRoute(
                nameof(GetCompany),
                new{companyId = returnDto.Id},
                returnDto);
}

在上述代码中,CreatedAtRoute方法的第一个参数,是返回可以查询到当前创建的资源的URI指向的Action方法,所以我们需要给该方法起一个名称代码如下:

 /// <summary>
 /// 获取指定公司
 /// </summary>
 /// <param name="companyId">公司id</param>
 /// <returns></returns>
 [HttpGet("{companyId}",Name =nameof(GetCompany) )]  //注意此处的Name属性
 //[Route("{companyId}")]   //使用这种方式也能表示路由,但是不常用
 public async Task<ActionResult<Company>> GetCompany(Guid companyId)

创建用于添加公司资源的Dto对象的代码如下:

 /// <summary>
 /// 创建公司的Dto对象
 /// </summary>
 public class CompanyAddDto
 {
     
     public string Name { get; set; }
     public string Introduction { get; set; }

 }

CompanyProfile.cs类中,映射 CompanyAddDto 与 表示实体 Comapny 的映射关系,代码如下:

/*
 * CompanyAddDto(原对象),Company(目标对象)的映射
 */
CreateMap<CompanyAddDto,Company>();

优化实现类添加公司部分的代码:

public void AddCompany(Company company)
{
    if (company == null)
    {
        throw new ArgumentException(nameof(company));
    }

    company.Id = Guid.NewGuid();

    if (company.Employees !=null)
    {
        foreach (var employee in company.Employees)
        {
            employee.Id = Guid.NewGuid();
        }

    }

    this._content.Add(company);
    //this._content.Companies.AddAsync(company);
}

调用创建公司资源接口:

调用
调用

调用结果:

调用结果
调用结果

返回的Handers:

返回的Handers
返回的Handers

我们可以看到在返回的Headers中已经包含了,可以再一次请求到当前插入的资源URI
Location Header :http://localhost:5000/api/Companies/663b20a2-dadc-4502-a99d-57a26810530b

请求当前资源
请求当前资源

故意不传参数查看插入结果,通过返回结果可知返回了400状态码,而这个自动化的过程,由[ApiController]特性实现.

调用结果
调用结果

创建子资源

一个公司一般认为有多个员工,那么公司在这个关系中,就相当于父资源,员工就相当于子资源.

编写接收传入参数的Dto,代码如下:


using System;
using Routine.Api.Entitle;

namespace Routine.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; }
    }
}

EmployeeProfile.cs类中配置 Employee 与 EmployeeAddDto的映射关系:

//配置映射
//第一个参数: 源对象
//第二个参数: 目标对象
CreateMap<EmployeeAddDto, Employee>();

编写创建子资源的API,代码如下:

[HttpPost]
public async Task<ActionResult<EmployeeDto>> CreateEmployeeForCompany(Guid companyId,[FromBody] EmployeeAddDto employee)
{
    if (!await this._companyRepository.CompanyExistsAsync(companyId))
    {
        return NotFound();
    }

    //此处的if可以不写因为框架已经保证了,当闯入的创建资源的参数无法被解析会自动返回415的Http状态码
    if (employee == null)
    {
        return BadRequest();
    }

    //使用AutoMapper将请求的Dto转换成Entity
    var entity = this._mapper.Map<Employee>(employee);

    this._companyRepository.AddEmployee(companyId,entity);

    await this._companyRepository.SaveAsync();

    var dtoToReturn = this._mapper.Map<EmployeeDto>(entity);

    //返回插入的资源
    //第一个参数:返回可找到当前资源的URI
    //第二个参数:路由的值
    //第三个参数:返回的是响应的Body
    return CreatedAtRoute(
        nameof(GetEmployeeForCompany),
        new { companyId = companyId, employeeId = dtoToReturn.Id },
        dtoToReturn);
}

根据返回的Headers我们需要给找到当前创建的资源的API起一个路由别名,所以我们需要修改GetEmployeeForCompany的路由规则特性,代码如下:

[HttpGet("{employeeId}", Name = nameof(GetEmployeeForCompany))]
public async Task<ActionResult<EmployeeDto>> GetEmployeeForCompany(Guid companyId, Guid employeeId)

调用及返回调用结果:

配置请求的Headers:

配置请求Headers
配置请求Headers

调用:

调用
调用

返回的结果:

返回的结果
返回的结果

返回的Headers:

返回的Headers
返回的Headers

我们注意到返回的Headers中包含了,Location信息. Location信息的值,就是可以再一次请求到创建的资源的URI.

posted @ 2020-04-11 12:32  HelloZyjS  阅读(150)  评论(0编辑  收藏  举报