使用ASP.NET Core 3.x 构建 RESTful API P3 对外合约

使用ASP.NET Core 3.x 构建 RESTful API P3 对外合约

为了和RESTful API进行交互, API的消费者需要使用到三个概念.

  • 资源的标识. URL
  • HTTP方法. 如: Get,Post,Put,Delete等.
  • 有效载荷(可选),英文就是PayLoad:比如当我们想创建资源的时候,Http请求里面,通常会带有创建资源的表述,或者当获取资源时,Http响应体即(Body)中会包含对资源的表述.
资源的命名

在合理的API命名上,我们应该使用名词,而不是动词,动词应该通过Http方法来表达.

例子1:

  • 需求: 我们看这样一个需求的例子:"我想获得系统里所有的用户".
  • 常见错误做法:api/getusers
  • 分析: 这句话的主要动词就是"获取",而想要获取的资源(也就是主要的名词)是"用户".
  • 正确的做法: Get api/user
人类能读懂
  • 还是上面那个需求: "我想获的系统里所有的用户."
  • 我们可以把url设计成 api/u 或者 api/ur (这样的写法不够友好,看不懂)
  • 所以建议的做法是足够友好,并且比较简短,例如:api/users
要体现资源的结构/关系
  • 假设如果后端API系统里面有若干种资源,而用户这个资源与其它的资源并没有直接的关系,这样的话获取用户资源的url应该是 api/users. 而不是api/products/users,也不是api/catalog/products/users,因为user和product或者catalog没有直接关系.
  • 通过id获取单个用户的url应该是:api/users/{urserId},而不是api/userid/users 这样写的好处是可以让Api具有和好的可预测性和一致性.

例子2:

  • 需求:系统里有两类资源,公司(Company) 和员工 (Employee),现在我想获取某个公司下所有的员工.
  • 分析:使用HTTP的GET. API 的URL在设计的时候需要体现公司和员工的包含关系.
  • 常见的错误做法: Get api/employees, Get api/employees/{companyId}
  • 建议的做法: Get api/companies/{companyId}/employees

例子3:

  • 需求:我想获取某个公司的某个员工信息.
  • 分析:使用HTTP的GET. API 的URL在设计的时候需要体现公司和员工的包含关系.
  • 常见的错误做法: Get api/employees/{employeeId}, Get api/Companies/{employeeid}等等
  • 建议的做法: Get api/Companies/{companyId}Employees/{employeeId}
自定义查询怎么命名
  • 需求: 我想获取所有的用户信息,并要求结果是按年龄从小到达进行排序的.
  • 常见错误的做法: Get api/users/orderby/age
  • 建议的做法: Get api/users?orderby=age
例外
  • 有一些需求总是无法满足的达到RESTful的约束.
  • 需求:"我想获取系统里所有用户的数量."
  • 妥协的做法: 例如: Get api/users/totalamountofuser

RESTful 风格的API 比较适合于CRUD这类业务的API.

好了下面是写代码的时间,我们打开p1中所创建的项目,并在其中创建一个Controller
类.

补充知识点:
HTTP 动词 不是在 Controller 里面的方法名上体现的.
约定: 当Controller中方法名的前缀如果是个动词如 GetCompanies 那么如果没有为此方法标注动词,那么默认就是以 [HttpGet] 的方式来请求此 Action.

IActionResult 接口: IActionResult 接口相当于订阅了一些合约,这些合约代表了 Action 方法返回的结果.

.Net CoreController类也会继承自ControllerBase类,他们主要的区别在与,ControllerBase类仅实现了,Controller(控制器)相关的功能,而Controller类,在拥有Controller(控制器)相关功能的基础上,还添加了对于View(视图)的支持.

因此在编写 WebAPi 时,我们创建的控制器,继承自 ControllerBase类即可.
在编写Mvc时,我们创建的控制器,需要继承自Controller类,因为其包含了对于view(视图)操作的实现.

特性 [ApiController] 的介绍
  • 这个特性是应用于Controller的,但是它其实并不是强制要使用的.
  • 使用 [ApiController] 会启用一下行为:
    • 要求使用属性路由(Attribute Routing)
    • 自动HTTP 400 响应. (在Action方法被传入model的时候,会有一个验证动作,model含有验证错误的信息,会自动触发HTTP 400 相应.)
    • 推断参数的绑定源 (自动推断Action方法的参数到底来自于哪一个绑定源,比如:[FromBody],[FromServices]等等).
    • Multipart/Form-data 请求推断.
    • 错误状态代码的问题详细信息.
  • 使用了 [ApiController] 特性之后,我们就不能在Startup.cs类的Configure方法中的端点中间件中统一配置路由模板了,需要我们在每个 Controller 以及每个 Action 上面单独的配置路由模板.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Routine.Api.Services;

namespace Routine.Api.Controllers
{
    [Route("api/Companies")]
    [ApiController]
    public class CompaniesController:ControllerBase
    {
        private readonly ICompanyRepository _companyRepository;

        public CompaniesController(ICompanyRepository companyRepository)
        {
            _companyRepository = companyRepository ?? throw new ArgumentNullException(nameof(companyRepository));
        }


        /// <summary>
        /// 获取所有公司信息
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> GetCompanies()
        {
            var companies = await this._companyRepository.GetCompaniesAsync();

            if (companies == null)
            {
                return NotFound();   
            }

            return Ok(companies);
        }
    }
}

代码编写好了,我们运行一下,发现报错了.

修正代码:

            /*
             * 端点中间件
             */
            app.UseEndpoints(endpoints =>
            {
                //endpoints.MapGet("/", async context =>
                //{
                //    await context.Response.WriteAsync("Hello World!");
                //});

                // 在使用了 [ApiController] 特性之后,我们就不需要再使用全局配置的路由表了,我们不应该再使用上述方法,
                // 而是应该使用下列方法来描述端点 (MapControllers 表示统一的路由模板)
                endpoints.MapControllers();
            });
posted @ 2020-03-26 22:34  HelloZyjS  阅读(234)  评论(0编辑  收藏  举报