使用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 Core
中Controller
类也会继承自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();
});
- 以上博文参考自
p3 对外合约