Restful是什么

REST

REpresentational State Transfer , 一组架构约束条件和原则

原则

  • 网络上的所有事物都被抽象为资源
  • 每个资源都有一个唯一的资源标识符
  • 同一个资源具有多种表现形式(xml,json 等)
  • 对资源的各种操作不会改变资源标识符
  • 所有的操作都是无状态的

RESTful Api

RESTful 翻译成中文为“REST 式的”,满足了 REST 架构风格的应用程序设计的 Api 则便是 RESTful Api,即 REST 式的 Api。

设计规范

考虑 Api 设计时,URI 中不能有动词,URI 的目的是定位资源,而具体的对资源的操作,是借助 HTTP 的动词完成,与早期 Api 设计相比,本身的思路是不同的,原来更多的是考虑函数式编程或者叫做面向行为的服务建模,比如 RPC,远程调用一个函数,那么 Api 设计便是会考虑为动词名词格式,而对于 REST 风格来讲,是面向资源的服务建模。而对于资源而言,可以是对象、数据或是查询服务。

HTTP动词

对于一个系统而言,对外提供的功能总体上划分为两类:
  • 获取系统资源,主要包括读取资源和资源描述信息。
  • 对系统资源进行变更,主要包括写入资源,对已有资源状态的变更,删除已有资源。

对于这其中使用到的一些动词,使用 HTTP 的动词描述来承担对资源执行的行为,动词通常使用以下几种。对于 HTTP1.1 规范中的其他几个动词(如 OPTIONS 等)则不再介绍。

  • GET: 获取目标资源
  • HEAD: 获取(传输)目标资源的元数据信息。该方法与 GET 相同,但是不传递内容体。
  • POST: 创建新资源,对于复杂查询而言,提交查询表单给查询服务也是常用 POST 的(当然其他几个能做的它也能做)。
  • PUT: 替换已有资源(整体)。
  • PATCH: 修改已有资源(局部)。
  • DELETE: 删除资源。

URI

统一资源标识符

对于资源的单复数格式,尽管规范是尽可能使用复数,但并没有说哪条纪律或是约束限制说一定要使用复数,这无需强制约束,按照自身统一即可。毕竟有些不可数名词,没有复数格式,那么还是沿用本身,而对于整体风格为复数下,却又显得格格不入。 

面向资源

资源的组织决定着 URI 的展示方式,对于底层数据库而言,也许 Order 模型有若干张表来支撑存储,对外总体是提供着 Order 服务。这样一来,如果按照底层数据库表来考虑 Api 设计,则会陷入无尽的关系处理中,比如 Order 下有 OrderItem,OrderItem 下有 OrderItemAttachment,如果按照这个思路去实现 Api 设计时,那么 URI 的设计上则会存在多级情况。
如果按照单表进行 URI 设计,那么则成了面向表服务建模,这又造成了底层的服务细节统统对外暴露,因此需要避免创建仅反映数据库内部结构的 API。
在领域驱动设计中,聚合这一概念,将具有强相关的实体和值对象纳入到一起,形成独立空间、业务逻辑内聚于聚合之中,同生共死。面向聚合进行 Api 设计,多级路由的嵌套结构缓和许多,如需求上考虑 Order 创建时一定需要有 OrderItem 的存在,那么则对于这两者而言是捆绑的关系,而对于 OrderItemAttachment 而言,不是必要的。

 

 

那么则可以独立设计聚合(此处忽略底层数据库中表是如何设计的,仅考虑聚合),URI 的设计也围绕着聚合这一资源来进行,这样一来,URI 的设计便成了如下结构

POST /Api/Orders 
{
    "locationId": 1,
    "productIds": [
        1,
        2,
        3
    ]
}

POST /Api/Orders/{id}/OrderItems 
{
    "productIds": [
        4,
        5,
        6
    ]
}

POST /Api/OrderItemAttachments 
{
    "orderItemId": 1,
    "fileUrl": "xxx"
}

嵌套层级结构不会太深,因为太深的层级结构往往也意味着这个聚合的设计或许存在一点问题。

约束设计

对于 Post、Put、Patch 和 Delete 这些操作来讲,面向聚合设计 URI 基本可以有路可循。
比如以下一些常见的 URI
 
POST /Api/Orders
POST /Api/Orders/{id}/OrderItems
POST /Api/OrderItemAttachment
PUT /Api/Orders/{id}
PUT /Api/Orders/{id}/OrderItems/{itemId}
PUT /Api/OrderItemAttachments/{id}
PATCH /Api/Orders/{id}/Address
PATCH /Api/Orders/{id}/OrderItems/{id}/Amount
PATCH /Api/OrderItemAttachments/{id}/FileUrl
DELETE /Api/Orders/{id}
DELETE /Api/Orders/Batches
DELETE /Api/Orders/{id}/OrderItems/{id}
DELETE /Api/OrderItemAttachments/{id}
POST /Api/Invites/emailTemplate

PATCH /Api/Invites/{id}/Sendmail //Sendmail 作为邮件服务资源
PATCH /Api/Notifications/{id}/MessageStatus
PATCH /Api/Notifications/MessageStatus/batches
PATCH /Api/Orders/{id}/OrderItem/{itemId}/PayStatus

POST /Api/Orders/exports //返回导出资源
POST /Api/exportServices //提交给导出服务资源
POST /Api/exportServices/Sendmail
POST /Api/InviteParseServices //提交给解析服务资源

...

当然也有一些夹杂着动词,习以为常的 Api 设计,如果习惯了,不想改变,仍然可以使用着动词(后续提到该部分违反约束),但若想改变,就得换个思路去考虑设计了。

如果按照这几个约束条件来看的话,仅当满足三个约束条件的才能认为是 RESTful Api,而满足一个或是两个约束条件的为 Http Api,那么我们或许是一直在追随 RESTful Api 的路上了。

 

 

面对这部分难以描述或是无法组织的接口,个人认为直接违反一些约束即可,总归是只有少部分接口仅满足一个到两个约束。二八原则,百分之八十的接口能够符合restful api规范设计即可。

状态码

HTTP 中使用状态码来表示着请求的成功与否,我们可以直接使用它,而无需在返回值中再包裹一层 code/message,尽管在 mvc 中,我也很喜欢这么做。

 
{
  "code":200,
  "message":"",
  "data":{
    
  }
}

对 HTTP 的状态码接触越多后,越发觉得思路偏了,不应该将请求响应的状态码与业务中行为的成功与否进行隔离开,因为 HTTP 本身是应用层协议(超文本移交协议),是为业务服务的。如何在网络层面上把一个请求发送出去,再接收到响应,这是 TCP 协议来保障的。假设网络层如果请求失败了,那么应用层都无法进行,因此结合状态码与返回内容(当出现异常时仍然返回状态码与错误描述信息)。如下 HTTP 的状态码覆盖了绝大部分场景。当客户端需要追踪问题时,查看对应请求的状态码,结合其对应的解释说明,便可以去定位相关的问题,当然,前提是真的返回了符合场景下的状态码。

版本号

对外提供的资源服务地址需要存在版本控制,以便于客户端应用能够访问到对应的资源,版本号的规划有如下几种方式,具体使用哪种得依靠具体的情况而分析:
  • 不考虑版本,内部使用、短暂的生命周期下不考虑资源的变更或是直接对资源本身进行了换新如此变更到新的 url 上。

 

  • 为每个资源的 URI 添加一个版本号
GET /Api/v2/Orders/{id}

 

  • 作为查询字符串参数来指定资源的版本
GET /Api/Orders/{id}?version=2
  • 在 http 的 header 中增加自定标头设置版本号。
GET /Api/Orders/{id}
Custom-Header: version=2

成熟度模型

 2008 年,Leonard Richardson 为 Web API 提出了以下 成熟度模型 :
  • Level 0: 定义一个 URI,所有操作是对此 URI 发出的 POST 请求。
  • Level 1: 为各个资源单独创建 URI。
  • Level 2: 使用 HTTP 方法来定义对资源执行的操作。
  • Level 3: 使用超媒体(HATEOAS: 「H」ypermedia 「A」s 「T」he 「E」ngine 「O」f 「A」pplication 「S」tate,参见 HATEOAS - Wikipedia )。

诚然,对于这个成熟度模型,我一般都只会去达到前三个级别,虽然 Roy Fielding明确表示 ,Level 3 才是真正的 RESTful Api,对于 Level 3 级别,其实并没有理解到其具体奥妙。因为我们面对的是 UI,用 UI 去链接操作,那么对于 Level 3 返回的超媒体而言,又如何表现呢?暂不去理解了。
 
参考地址:
https://mp.weixin.qq.com/s/woXV8bn7iIOt2fHPtkSNAQ 
 

posted on 2022-06-21 17:02  爱吃柠檬不加糖  阅读(196)  评论(0编辑  收藏  举报

导航