RESTful发展背景及简介

网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备…)。因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"APIFirst"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。

REST(Representational State Transfer)表述性状态转换,REST指的是一组架构约束条件和原则。 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。

RESTful 是一种网络应用程序的设计风格和开发方式,给出一种约定的标准,包含 api 接口规范、命名规则、URL、返回值、授权验证等,以下介绍几种主要的设计规范,我们在项目编写当中需要尽可能的去遵守。


正确使用 HTTP 请求方法

  • GET:读取(Read)
  • POST:新建(Create)
  • PUT:更新(Update)
  • PATCH:更新(Update),通常是部分更新
  • DELETE:删除(Delete

URL

URI规范

不用大写;
用中杠-不用下杠_;
参数列表要encode;
URI中的名词表示资源集合,使用复数形式。

URI路径

既然上面 HTTP 请求方法已经为我们指定了请求的动作,那么请求的 url 路径只需要指定好需要请求的资源就可以了,它是名词,而非动词,或者动宾,比如 /articles 就是正确的,而下面的 URL 不是名词,所以是错误的:

  1. /getArticles
  2. /createNewCar
  3. /deleteAllRedCars

URL格式

URL 是不区分单数和复数的,通常的做法是当读取一个集合,比如 GET /articles,则表示读取所有文章,GET /articles/3 则表示读取某一篇文章。

实际情况中我们应该避免 URL 嵌套过深

常见的情况是,资源需要多级分类,因此很容易写出多级的 URL,比如获取某个作者的某一类文章。

GET /authors/12/categories/2

这种 URL 不利于扩展,语义也不明确,往往要想一会,才能明白含义。更好的做法是,除了第一级,其他级别都用查询字符串表达。

GET /authors/12?categories=2

下面是另一个例子,查询已发布的文章。我们可能会设计成下面的 URL。

GET /articles/published

查询字符串的写法明显更好。

GET /articles?published=true


服务器响应

接口的 API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。所以,服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json。 目前的前后端开发大部分数据的传输格式都是 JSON,因此定义一个统一规范的数据格式有利于前后端的交互与 UI 的展示。

Request

HTTP方法通过标准HTTP方法对资源CRUD:

安全性和幂等性

  1. 安全性:不会改变资源状态,可以理解为只读的;
  2. 幂等性:执行1次和执行N次,对资源状态改变的效果是等价的。

安全性/幂等性
GET √ √
POST × ×
PUT × √
DELETE × √

Format

只用以下常见的3种body format:

1.Content-Type: application/json

POST /v1/animal HTTP/1.1
Host: api.example.org
Accept: application/json
Content-Type: application/json
Content-Length: 24
 
{   
  "name": "Gir",
  "animalType": "12"
}

2.Content-Type: application/x-www-form-urlencoded (浏览器POST表单用的格式)

POST /login HTTP/1.1
Host: example.com
Content-Length: 31
Accept: text/html
Content-Type: application/x-www-form-urlencoded
 
username=root&password=Zion0101

3.Content-Type: multipart/form-data; boundary=—-RANDOM_jDMUxq4Ot5 (表单有文件上传时的格式)

Content Negotiation

资源可以有多种表示方式,如json、xml、pdf、excel等等,客户端可以指定自己期望的格式,通常有两种方式:

1.http header Accept:(q为各项格式的偏好程度)

Accept:application/xml;q=0.6,application/atom+xml;q=1.0

2.url中加文件后缀:/zoo/1.json

Response

response 的 body 直接就是数据,不要做多余的包装

错误处理

  1. 不要发生了错误但给2xx响应,客户端可能会缓存成功的http请求;
  2. 正确设置http状态码,不要自定义;
  3. Response body 提供 1) 错误的代码(日志/问题追查);2) 错误的描述文本(展示给用户)。

对第三点的实现稍微多说一点:

Java 服务器端一般用异常表示 RESTful API 的错误。API 可能抛出两类异常:业务异常和非业务异常。业务异常由自己的业务代码抛出,表示一个用例的前置条件不满足、业务规则冲突等,比如参数校验不通过、权限校验失败。非业务类异常表示不在预期内的问题,通常由类库、框架抛出,或由于自己的代码逻辑错误导致,比如数据库连接失败、空指针异常、除0错误等等。

业务类异常必须提供2种信息:

  • 如果抛出该类异常,HTTP 响应状态码应该设成什么;
  • 异常的文本描述;

在Controller层使用统一的异常拦截器:

  • 设置 HTTP 响应状态码:对业务类异常,用它指定的 HTTP code;对非业务类异常,统一500;
  • Response Body 的错误码:异常类名
  • Response Body的错误描述:对业务类异常,用它指定的错误文本;对非业务类异常,线上可以统一文案如“服务器端错误,请稍后再试”,开发或测试环境中用异常的stacktrace,服务器端提供该行为的开关。

常用的http状态码及使用场景

状态码 / 使用场景
400 bad request 常用在参数校验
401 unauthorized 未经验证的用户,常见于未登录。如果经过验证后依然没权限,应该 403(即 authentication 和 authorization 的区别)。
403 forbidden 无权限
404 not found 资源不存在
500 internal server error 非业务类异常
503 service unavaliable 由容器抛出,自己的代码不要抛这个异常

Last modification:November 13, 2024
如果觉得我的文章对你有用,您可以给博主买一杯果汁,谢谢!