使用ASP.NET Core 3.x 构建 RESTful API - 3.1 资源命名

 

以前讲了RESTful API的统一资源接口这个约束,里面提到了资源是经过URI来进行识别的,每一个资源都有本身的URI。URI里还涉及到资源的名称,而针对资源的名称却没有一个标准来进行规范,可是业界仍是有一些最佳实践的。那么咱们首先看看这些最佳实践对资源命名是如何建议的。 web

 

资源命名 

下面让咱们来看看RESTful API资源命名的一些最佳实践。 json

 

使用名词,而不是动词 

一个资源的URI表明的是一个实际上或概念上存在的东西,所以,它应该是名词,因此也就不该该出现动词,动词应该使用HTTP方法来表达。 后端

 

需求咱们看这样一个需求的例子:“我想得到系统里全部的用户”。 api

常见错误作法你可能把API的URI设计成这样:api/getusers。这样的设计是很差的,由于里面出现了一个动词get。 mvc

分析这个句话的主要动词就是“获取”,而想要获取的资源(也就是主要的名词)是“用户”。 async

正确的作法需求里面主要的动词应该经过HTTP方法来体现,“获取”对应的HTTP方法就是GET。而“用户”这个资源能够用英文user或者users来表示(是否使用复数一直存在争议,两种方法都行,但你在使用的时候须要保持一致)。因此正确的uri应该是 GET api/user。 函数

 

人类能读懂 

仍是上面那个需求:“我想得到系统里全部的用户”。 ui

咱们能够把uri设计成 api/u 或者 api/ur。可是这样设计的话,对API的消费者来讲很是的不友好,由于不能直观的看出来它到底表明的是什么资源,多是user,也多是university。 spa

因此建议的作法是要足够友好,而且比较简短,例如:api/users 设计

 

要体现资源的结构关系 

假设若是后端API系统里面有若干种资源,而用户这个资源与其它的资源并无直接的关系,这样的话获取用户资源的uri应该是 api/users。而不是 api/products/users,也不是api/catalogs/products/users,由于user和product或者catalog没有直接的关系。 

经过id获取单个用户的uri应该是:api/users/{userId},而不是api/userid/users。 

这样写的好处是可让API具备很好的可预测性和一致性。 

 

需求1系统里有两类资源,公司(Company)和员工(Employee),它们俩是包含关系,也就是一个公司包含多个员工。如今我想获取某个公司下全部的员工信息。 

分析这里的主要动词仍是“获取”,因此咱们可使用HTTP的GET。而这里的资源有两个,分别是公司和员工,并且它们是包含关系:一个公司包含多个员工或者说一个公司是一个员工的集合。因此API的URI在设计的时候须要体现这种包含关系。 

常见的错误作法若是你想得到公司这个资源,我想你如今应该不会出错,uri应该是 api/companies。而想要获取某个公司下的员工,常见的错误作法有:api/employees,api/employees/{companyId}等等。这些设计很是很差是由于它没法体现出Company和Employee之间的结构关系。 

建议的作法:须要体现Company和Employee之间的关系,因此uri应该是GET api/companies/{companyId}/employees。这样作直接体现出了Company和Employee之间的结构关系,并且也体现出了一个Company就是一个Employee的集合体。 

 

需求2:我想获取某个公司的某个员工信息。 

常见的错误作法:api/employees/{employeeId},api/companies/{employeeId}等等。这些作法都没法体现出Company和Employee之间的关系。 

建议的作法:api/companies/{companyId}/employees/{employeeId} 

 

自定义查询怎么命名 

咱们常常会遇到这样的需求,好比获取按照某个资源排序后的资源,或者按照某些条件过滤后的资源。这时候应该怎对资源进行命名呢? 

 

需求:“我想获取全部的用户信息,并要求结果是按年龄从小到大进行排列的”。 

常见错误的作法:api/users/orderby/age。以前说了,uri里面使用的都应该是名词,若是按照这个uri的结构来看,那么orderby和age就应该是另外两个资源,而且users包含orderby,orderby包含age,这显然是错误的。 

建议的作法:api/users?orderby=name,这样设计更合理一些。这里使用了query string做为查询参数进行排序。 

 

例外 

有一些需求老是没法知足的达到RESTful的约束。 

需求:“我想获取系统里全部用户的数量”。 

妥协的作法:咱们确实能够先经过 GET api/users来获取系统里全部的用户信息,而后再算出用户的数量,可是这样作也太浪费资源而且效率也过低了。咱们也很难使用某个名词来表示这个需求的资源。例如:api/users/totalamountofuser。这样的uri按理说就表明着咱们将会获取到一个集合资源,里面是一堆数字,但针对这个需求,我也没有特别好的办法让uri命名彻底符合RESTful的约束,因此针对这个需求,我使用的就是这个uri。 

 

Demo

下面咱们就来实践一下。打开以前的项目,并创建CompaniesController 

ApiController] 
I reference 
public class CompaniesController 
ControllerBase 
private readonly ICompanyRepository 
companyRepository; 
O references 
public CompaniesController(ICompanyRepository companyRepository) 
companyRepository - 
companyRepository ?? 
throw new ArgumentNullException(nameof(companyRepository)); 
O references 
public async Task<IActionResult> GetCompanies() 
await companyRepository .GetCompaniesAsync() ; 
var companies 
return new JsonResult(companies);

这里有6个地方比较关键,咱们挨个看一下: 

  1. RESTful API 或者其它Web APIController都应该继承于 ControllerBase 这个类(点此查看详细的官方文档),而不是Controller这个类。 

    1. Controller类继承于ControllerBaseController添加了对视图的支持,所以它更适合用于处理 MVC Web 页面,而不是 Web API。可是若是你的Controller须要同时支持MVC Web页面和Web API,那么这时候就应该继承于Controller这个类。 

    2. ControllerBase 类提供了不少用于处理 HTTP 请求的属性和方法。 例如,ControllerBase.CreatedAtAction 返回 201 状态代码。关于ControllerBase的属性和方法的详细列表,请查看官方参考文档 

  2. [ApiController]。这个属性是应用于Controller的,它其实并非强制的,可是它提供了一些帮助,使得Web API的开发体验更好。详细教程请点击 [ApiController]的官方文档。在Controller上面添加了[ApiController]属性以后,就会启用如下行为: 

    1. 要求使用属性路由(Attribute Routing)。也就是不能经过StartupConfigure方法统一配置路由模板。这部分的详细介绍请点击:官方文档 

    2. 自动HTTP 400响应。也就是Action方法传入的model含有验证错误的时候,自动触发HTTP 400响应。这部分的详细介绍请点击:官方文档 

    3. 推断参数的绑定源。它将会推断出Action方法的参数到底来自哪一个绑定源,例如[FromBody][FromForm]等等。这部分的详细介绍请点击:官方文档 

    4. Multipart/form-data 请求推断使用 [FromForm] 属性批注操做参数时,[ApiController] 属性应用推断规则,它会推断 multipart/form-data 请求内容类型。这部分的详细介绍请点击:官方文档 

    5. 错误状态代码的问题详细信息MVC 会将错误结果(状态代码为 400 或更高的结果)转换为状态代码为 ProblemDetails 的结果。 ProblemDetails 类型基于 RFC 7807 规范,用于提供 HTTP 响应中计算机可读的错误详细信息。这部分的详细介绍请点击:官方文档 

  3. 咱们须要经过构造函数注入ICompanyRepository,并把它存放在一个只读的字段里面。 

  4. 若是注入的ICompanyRepository的实例为null,那么就抛出一个ArgumentNullException 

  5. 想要返回数据结果,咱们须要在Controller里面添加一个Action方法。我暂时把它的返回类型写为IActionResult(详细介绍请点击官方文档)。IActionResult里面定义了一些合约,它们能够表明Action方法返回的结果。 

  6. 我暂时只想把结果序列化为JSON格式并返回,这里我new了一个JsonResult参考文档),它能够作这项工做。 

 

目前我只作了这几项最基本的工做:建立Controller,注入Repository,建立Action方法并返回结果。下面运行一下看看报了什么错: 

这是由于GetCompanies这个Action方法并无使用属性路由(Attribute Routing)。关于路由这部分,下一篇文章再介绍。

相关文章
相关标签/搜索