RESTful API设计原则与规范

一、背景与基础概念 2

二、RESTful API应遵循的原则 3

1、协议(Protocol) 3

2、域名(ROOT URL) 3

3、版本(Versioning) 3

4、路径(Endpoints) 3

5、HTTP动词(HTTP Verbs) 4

6、过滤信息(Filtering) 5

7、状态码(Status Codes) 5

8、错误处理(Error handling) 6

9、返回结果(Response) 6

10、使用HATEOAS的Hypermedia API 6

11、认证(Authentication) 7

三、Swagger API标准 7

REST,即Representational State Transfer的缩写。RESTful架构,是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制,所以正得到越来越多网站的采用。如果一个架构符合REST原则,就称它为RESTful架构。

本文即将描述的,即是创建RESTful架构的API所要遵循的原则与规范。

一、背景与基础概念

Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。

  • 资源(resource):网络上的一个实体或者说是一个具体信息,可以是一段文本、一张图片、一首歌曲、一种服务。
  • 统一资源定位符(URI,Universal Resource Identifier):一个资源的识别符或者说是一个地址,通过URI你可以定位到特定的资源。要获取这个资源,需要访问它的URI,因此,URI就成了每一个资源的地址或独一无二的识别符。
  • 状态转换(State Transfer): 所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。客户端与服务器互动的过程,通常涉及到服务器端数据和状态的变化过程,比如文件被修改,访问数量增加等。使用的是标准的 HTTP 方法,Http标准中定义的最主要四个动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:

- GET: 用来获取资源

- POST: 用来新建资源

- PUT: 用来更新资源

- DELETE: 用来删除资源

  • Hypermedia 是应用程序状态的引擎,资源表示通过超链接互联。

二、RESTful API应遵循的原则

1、协议(Protocol)

API与用户的通信协议,尽量使用HTTPs协议。HTTPs协议的所有信息都是加密传播,第三方无法窃听,具有校验机制,一旦被篡改,通信双方会立刻发现,配备身份证书,防止身份被冒充。

2、域名(ROOT URL)

应该尽量将API部署在专用域名之下。

https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

https://example.org/api/

3、版本(Versioning)

应该将API的版本号放入URL。

https://api.example.com/v1/

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

注:需要注意版本规划,以便以后API的升级和维护。使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5。

4、路径(Endpoints)

路径表示API的具体网址URL。在RESTful架构中,每个URL代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与代表的对象名称对应。一般来说,某一同种记录的”集合”(collection),所以API中的名词也应该使用复数。

具体细则:

1、使用名词而不是动词。举例来说,某个URL是/cards/show/1,其中show是动词,这个URL就设计错了,正确的写法应该是/cards/1,然后用GET方法表示show。如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:POST /accounts/1/transfer/500/to/2。正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:POST /transaction?from=1&to=2&amount=500.00。

2、使用复数名词。不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。

举例:

/cars 而不是 /car

/users 而不是 /user

/products 而不是 /product

/settings 而不是 /setting

3、使用子资源表达关系。如果一个资源与另外一个资源有关系,使用子资源。

举例:

GET /cars/911/drivers/      返回 car 911的所有司机

GET /cars/911/drivers/8    返回 car 911的8号司机

5HTTP动词(HTTP Verbs)

对于资源的具体操作类型,由HTTP动词表示。常用的HTTP动词有下面五个:

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

注:Get方法和查询参数不应该涉及状态改变。使用PUT, POST 和DELETE方法而不是 GET 方法来改变状态。

6、过滤信息(Filtering

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。为集合提供过滤、排序、选择和分页等功能。

下面是一些常见的参数。

  • ?limit=10:指定返回记录的数量
  • ?offset=10:指定返回记录的开始位置。
  • ?pageNumber=2&perSize=100:指定第几页,以及每页的记录数。
  • ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  • ?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的

注:

①移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。

②为了将总数发给客户端,使用订制的HTTP头: X-Total-Count.

7、状态码(Status Codes

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT - [DELETE]:用户删除数据成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*]:表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH]:当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

8、错误处理(Error handling

如果状态码是4xx,就应该向用户返回出错信息。尽量使用详细的错误包装信息:

{

"errors": [

{

"userMessage": "Sorry, the requested resource does not exist",

"internalMessage": "No car found in the database",

"code": 4xx,

"more info": "http://dev.example.com/api/v1/errors/12345"

}

]

}

9、返回结果(Response

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • PATCH /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档

10、使用HATEOASHypermedia API

RESTful API最好使用Hypermedia as the Engine of Application State(超媒体作为应用状态的引擎),即返回结果中提供链接,连向其他API方法,超文本链接可以建立更好的文本浏览,使得用户不查文档,也知道下一步应该做什么。

比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。

{"link": {

"rel":   "collection https://www.example.com/zoos",

"href":  "https://api.example.com/zoos",

"title": "List of zoos",

"type":  "application/vnd.yourformat+json"

}}

上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

11、认证(Authentication

API的身份认证尽量使用OAuth 2.0框架。

三、Swagger API标准

API的文档管理和信息描述,将使用Swagger进行。

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。Swagger的目标是对REST API定义一个标准的和语言无关的接口,可让人和计算机无需访问源码、文档或网络流量监测就可以发现和理解服务的能力。

Swagger规范定义了一组描述一个API所需的文件格式,类似于描述Web服务的WSDL。通过Swagger进行REST API的正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger消除了调用服务时可能会有的猜测。

注:Microsoft,PayPal等公司也已经引入Swagger 来定义自己的REST API 文档。

Swagger API可以使用YAML或JSON来表示。

Swagger这类API文档工具可以满足下列需求:

  • 支持API自动生成同步的在线文档
  • 这些文档可用于项目内部API审核
  • 方便测试人员了解API
  • 这些文档可作为客户产品文档的一部分进行发布
  • 支持API规范生成代码,生成的客户端和服务器端骨架代码可以加速开发和测试速度

通常情况下,API的Swagger描述为JSON文件,也可使用YAML描述的文件。

附Swagger文件示例:

{

"swagger": "2.0",

"info": {

"version": "1.0.0",

"title": "Swagger Petstore (Simple)",

"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",

"termsOfService": "http://helloreverb.com/terms/",

"contact": {

"name": "Swagger API team",

"email": "foo@example.com",

"url": "http://swagger.io"

},

"license": {

"name": "MIT",

"url": "http://opensource.org/licenses/MIT"

}

},

"host": "petstore.swagger.io",

"basePath": "/api",

"schemes": [

"http"

],

"consumes": [

"application/json"

],

"produces": [

"application/json"

],

"paths": {

"/pets": {

"get": {

"description": "Returns all pets from the system that the user has access to",

"operationId": "findPets",

"produces": [

"application/json",

"application/xml",

"text/xml",

"text/html"

],

"parameters": [

{

"name": "tags",

"in": "query",

"description": "tags to filter by",

"required": false,

"type": "array",

"items": {

"type": "string"

},

"collectionFormat": "csv"

},

{

"name": "limit",

"in": "query",

"description": "maximum number of results to return",

"required": false,

"type": "integer",

"format": "int32"

}

],

"responses": {

"200": {

"description": "pet response",

"schema": {

"type": "array",

"items": {

"$ref": "#/definitions/pet"

}

}

},

"default": {

"description": "unexpected error",

"schema": {

"$ref": "#/definitions/errorModel"

}

}

}

},

"post": {

"description": "Creates a new pet in the store.  Duplicates are allowed",

"operationId": "addPet",

"produces": [

"application/json"

],

"parameters": [

{

"name": "pet",

"in": "body",

"description": "Pet to add to the store",

"required": true,

"schema": {

"$ref": "#/definitions/newPet"

}

}

],

"responses": {

"200": {

"description": "pet response",

"schema": {

"$ref": "#/definitions/pet"

}

},

"default": {

"description": "unexpected error",

"schema": {

"$ref": "#/definitions/errorModel"

}

}

}

}

},

"/pets/{id}": {

"get": {

"description": "Returns a user based on a single ID, if the user does not have access to the pet",

"operationId": "findPetById",

"produces": [

"application/json",

"application/xml",

"text/xml",

"text/html"

],

"parameters": [

{

"name": "id",

"in": "path",

"description": "ID of pet to fetch",

"required": true,

"type": "integer",

"format": "int64"

}

],

"responses": {

"200": {

"description": "pet response",

"schema": {

"$ref": "#/definitions/pet"

}

},

"default": {

"description": "unexpected error",

"schema": {

"$ref": "#/definitions/errorModel"

}

}

}

},

"delete": {

"description": "deletes a single pet based on the ID supplied",

"operationId": "deletePet",

"parameters": [

{

"name": "id",

"in": "path",

"description": "ID of pet to delete",

"required": true,

"type": "integer",

"format": "int64"

}

],

"responses": {

"204": {

"description": "pet deleted"

},

"default": {

"description": "unexpected error",

"schema": {

"$ref": "#/definitions/errorModel"

}

}

}

}

}

},

"definitions": {

"pet": {

"type": "object",

"required": [

"id",

"name"

],

"properties": {

"id": {

"type": "integer",

"format": "int64"

},

"name": {

"type": "string"

},

"tag": {

"type": "string"

}

}

},

"newPet": {

"type": "object",

"required": [

"name"

],

"properties": {

"id": {

"type": "integer",

"format": "int64"

},

"name": {

"type": "string"

},

"tag": {

"type": "string"

}

}

},

"errorModel": {

"type": "object",

"required": [

"code",

"message"

],

"properties": {

"code": {

"type": "integer",

"format": "int32"

},

"message": {

"type": "string"

}

}

}

}

}

Swagger文件的构成以及规范信息,在Swagger官方的规范指导《Swagger RESTful API文档规范》中有详细描述,请参看。

http://swagger.io/specification/

RESTful API设计原则与规范的更多相关文章

  1. RESTful API 设计原则

    http://www.ruanyifeng.com/blog/2014/05/restful_api.html http://www.ruanyifeng.com/blog/2011/09/restf ...

  2. RESTful API设计概要

    一.简介 1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次出现在2000年Roy Fiel ...

  3. (转)RESTful API 设计最佳实践

    原文:http://www.oschina.net/translate/best-practices-for-a-pragmatic-restful-api 数据模型已经稳定,接下来你可能需要为web ...

  4. 微服务指南走北(三):Restful API 设计简述

    API的定义取决于选择的IPC通信方式,假设是消息机制(如 AMQP 或者 STOMP).API则由消息频道(channel)和消息类型.假设是使用HTTP机制,则是基于请求/响应(调用http的ur ...

  5. RESTful API 设计总结

    RESTful API 设计总结 @(技术-架构)[API, 规范, 设计] RESTful的接口设计风格应用的越来越广泛,包括Spring Cloud等微服务架构平台之间的调用都是以RESTful设 ...

  6. Restful API 接口设计标准及规范

    Restful API 接口设计标准以及规范 RESTful概念 理解和评估以网络为基础的应用软件的架构设计,得到一个功能强.性能好.适宜通信的架构.REST指的是一组架构约束条件和原则." ...

  7. 理解RESTful架构——Restful API设计指南

    理解RESTful架构 Restful API设计指南 理解RESTful架构 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式 ...

  8. GOTO Berlin: Web API设计原则

    在邮件列表和讨论区中有很多与REST和Web API相关的讨论,下面仅是我个人对这些问题的一些见解,并没有绝对的真理,InnoQ的首席顾问Oliver Wolf在GOTO Berlin大会上开始自己的 ...

  9. RESTful API 设计指南 (转)

    RESTful API 设计指南 2016-02-23 ImportNew (点击上方公号,可快速关注) 作者:阮一峰 链接:http://www.ruanyifeng.com/blog/2014/0 ...

随机推荐

  1. Web静态服务器

    Web静态服务器 编程微刊 Web静态服务器-1-显示固定的页面 #coding=utf-8 import socket def handle_client(client_socket): " ...

  2. Vue中v-for不绑定key会怎样

    Vue的v-for不绑定key,默认行为和绑定key="index"是差不多的,官方没有默认这种行为的情况下,会导致所有列表DOM重新渲染.key="index" ...

  3. C# await 高级用法

    原文:C# await 高级用法 本文告诉大家 await 的高级用法,包括底层原理. 昨天看到太子写了一段代码,我开始觉得他修改了编译器,要不然下面的代码怎么可以编译通过 await "林 ...

  4. Java类、实例的初始化顺序

    今晚是阿里巴巴 2013 校园招聘的杭州站笔试.下午匆忙看了两张历年试卷,去现场打了瓶酱油. 题目总体考察点偏基础,倒数第二题(Java 附加题)比较有趣,考察了 Java 初始化机制的细节,在此摘录 ...

  5. Method for finding shortest path to destination in traffic network using Dijkstra algorithm or Floyd-warshall algorithm

    A method is presented for finding a shortest path from a starting place to a destination place in a ...

  6. 解决:eth0安装插卡无法自己主动,网卡的配置信息不network-scripts于

    问题方案:eth0安装插卡无法自己主动,网卡的配置信息不network-scripts于 解决: 1>vi /etc/rc.d/rc.loacl 最后加 ifup eth0 2>reboo ...

  7. Swift 分类 结构体

    感谢原作者:http://www.cocoachina.com/newbie/basic/2014/0612/8780.html 类和结构体是人们构建代码所用的一种通用且灵活的构造体.为了在类和结构体 ...

  8. WPF 超长文本的来回滚动

    原文:WPF 超长文本的来回滚动 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/article/details/8362 ...

  9. Java文件运用

    1.使用java修改文件内容: package fileopt; import java.io.BufferedReader; import java.io.BufferedWriter; impor ...

  10. Methods and Systems for Enhancing Hardware Transactions Using Hardware Transactions in Software Slow-Path

    Hybrid transaction memory systems and accompanying methods. A transaction to be executed is received ...