如何编写相对标准的后端项目之设计 Restful API

原文出处: koala bear

本文主要介绍 Restful 风格,如何设计 HTTP Restful API,以及在设计过程中的一点经验之谈。文章有点枯燥,敬请谅解。最后建议在设计 HTTP API 时,遵循 Restful API 风格。

理解 Restful Style

在 web 飞速发展的环境下,Roy Fielding 于 2000 年在博士论文 Architectural Styles and the Design of Network-based Software Architectures 提出 **R ****E **presentational **S **tate Transfer ( REST ) 概念,它倡导一种新的 web 架构风格,具有面向资源、松耦合、无状态、易扩展等特点,如今被广泛的应用。

那么什么是 REST 风格呢,论文的第五章节 Representational State Transfer (REST) 做了详细说明,其中 uniform interface 是 REST 架构的最主要特征。

The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components…….

In order to obtain a uniform interface, multiple architectural constraints are needed to guide the behavior of components. REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.

注意前两个约束:identification of resources 和 manipulation of resources through representations,它们表明 uniform interface(即 RESTful API) 的主体是 resource,即 REST 是面向 resource 的。那么什么是 resource 呢?任何能够被命名的事物都可称为 resource,比如某个文本、图像,甚至某种服务。在 web 中,我们采用 URI 来指代某个 resource。顾名思义,representation 就是资源的表现形式,比如图像资源的表现形式可为 JPEG image,文本资源的表现可为 text。

那么第三个约束 self-descriptive messages 表示什么呢?论文是这样解释的,即采用 standard methods(如 HTTP Method) 和 media types(如 HTTP Content type) 来表示操作类型,举例来说,我们可以用 GET 方法表示查询某个资源,用 DELETE 方法表示删除某个资源。

第四个约束 hypermedia as the engine of application state 表示 application 的状态由 request 决定,即客户端通过发送 request 来改变 application 的状态。以 HTTP POST 为例,客户端可以新建一个 resource,因而改变了 application 的状态。

总结而言,uniform interface 的约束条件定义了 web application API 的风格,即 RESTful API,这种 API 是面向 resource 的,利用(HTTP)标准方法来描述操作,客户端通过 representation 来操作 resource,从而转化 resource 的状态。

推荐阅读

如何设计 Restful API

上节简单的介绍了 Restful 的核心特征,本文从实践角度出发,围绕 URI,HTTP Method,Response Code,序列化和安全等,并结合一个具体的案例,谈谈 Http Restful API 设计的经验。

比如某个账号系统(http://example.com),通常需要增删改查用户,我们就以增删改查用户(user)为例,详细的介绍Restful API 的设计。

URI 设计

作为 Restful 的核心特性,uniform interface 是面向资源的,我们用 URI 来指代某个资源。比如,我们用如下 URI 代表账号系统用户 john:

http://example.com/users/john
为了简便,后续例子可能会省略域名,用 /users/john 表达相同含义。

查询用户 john 的请求如下。

GET /users/john

更新用户 john 的请求如下。

PUT /users/john

删除用户 john 的请求如下。

DELETE /users/john

从上可以看出,不论是查询还是删除用户 john,我们都用相同的 URI 来表指代这个用户(资源),采用不同的 Http Method 来表示不同的操作类型。既然 /users/john 指代的是用户 john,顾名思义,/users 指代的是所有的用户,所以可以用如下请求查询所有的用户信息。

GET /users

因为 URI 代表的是某个资源,而资源本身是个名词,所以在设计 URI 时,也应该用名词,切记不要使用动词等词语。比如,如下 URI 是面向动作的,不满足 Restful 风格约束。

/users/getuser

帐户系统在不同阶段可能会有不同的版本,比如 v1,v2,我们通常把版本号放在前面,比如:

/v1/users/john /v2/users/john

如果需要查询某些符合要求的资源,可在 URI 的 paras 模块设置查询参数,如查询所有性别为女的用户

GET /users?gender=female

如果返回结果过多,可以加上分页功能,如查询自 john 起的 10 位用户。

GET /users?limit=10&marker=john

此外,在设计 uri 时,还应该注意如下要点:

  • 尽量用小写:比如用 john,最好不要用 John。
  • 用中杠 -,不用下杠 _

HTTP Method

HTTP 协议定义了 8 个 Method,在 Restful API 设计中,我们常用 POST,DELETE,PUT,GET 四方法来操作资源。其中 POST 表示创建资源,DELETE 表示删除某个资源,PUT 表示更新某个资源,GET 表示查询资源。例如,当我们要创建用户 andy 时,其 API 如下:

POST /uesrs Body: {"user": "andy", ...}

在设计 Restful API 时,应该遵循 HTTP 关于 “安全性” 和 “幂等性” 的要求。

  • 安全性:无论请求多少次,都不会改变资源的状态。比如 GET 操作,无论执行多少遍,都不会改变资源的状态。所以对于 GET 类 API,在编写应用端代码时,切记要尽可能避免出现删除或者更新数据的逻辑。
  • 幂等性:无论是执行一次,还是执行多次,效果是等价的,比如 DELETE,PUT 操作。以 DELETE 操作为例,删一次和删多次,效果都是该资源不存在了。

在 HTTP 协议,不同 Method 在 “是否有 request body” 和 “是否有 response body” 有所差异。总结如下表:

Method 安全性 幂等性 是否有 request body 是否有 response body
POST
DELETE 最好无
PUT Option
GET

Response Code

之所以着重谈谈 Response Code,是因为看到部分同学在设计 API 的时候,常常自己定义 Response Code。例如,查询某个不存在的用户 tom 时,其 Response 为:

GET /users/tom Response Code: 200 OK Body: {"error": {"message": "User tom not found.", "code": 100}}

在上述查询某个不存在的资源的例子中,其 Response Code 为 200,此外,还在 Body 中定义了代号为 100 的返回码。事实上,这种做法非常容易给用户造成困惑:首先,200 表示查询成功,而本例的查询结果却是失败的;再次,自定义的 100 的含义是什么?如果没有相关说明文档,用户是不得而知的。所以,清晰明了的返回值应该设计为如下:

Response Code: 404 Not Found Body: {"error": {"message": "User tom not found."}}

设计 API 时,请先深入理解 HTTP Response Code,充分利用它们代表的含义,除非特别需要,尽量不要去自己定义额外的返回码,以免引起误解。让我们一起回顾下常见 HTTP Response Code 的含义。

请求成功:

  • 200 OK: 请求已成功,Body 有返回内容。多用作 GET Method 的 API 的返回码。
  • 201 Created: 请求已经被实现,资源被创建。多用作 POST Method 的同步类型 API 的返回码。
  • 202 Accepted: 服务器已接受请求,但尚未处理。多用作 POST Method 异步类型 API 的返回码。
  • 204 No Content: 服务器成功处理了请求,没有返回任何内容。用多于 DELETE/PUT Method 的 API 的返回码。

因客户端原因导致请求失败:

  • 400 Bad Request: 如参数错误,格式错误
  • 401 Unauthorized: 用户未被认证,如用密码错误,证书错误
  • 403 Forbidden: 用户权限不够
  • 404 Not Found: 服务端无此资源。通常为 URL 不存在,或者某个 Method 不存在
  • 409 Conflict: 请求存在冲突无法处理该请求

因服务端原因导致请求失败:

  • 500 Internal Server Error: 服务端错误消息,服务器遇到了一个未曾预料的状况。这是最常用的服务端错误代码
  • 501 Not Implemented: 服务器不支持当前请求所需要的某个功能
  • 503 Service Unavailable: 如服务器维护或者过载等

编码

ASCII 编码计算机最早采用的字符编码,随着计算机在多个国家普及,几乎每个语言都有一套或者多套自己的编码 ,如汉字的 GBK 编码,日语的 Shift_JIS 编码。这些编码方案各不相同,非常容易造成兼容性问题。Unicode 的诞生很好的解决了上述问题,它使计算机能够跨语言、跨平台进行文本转换和处理。Unicode 字符集包含了世界上所有文字和符号,它有 utf-8,utf-16 等编码方案,其中 utf-8 是最为常用的编码方案。从个人经验而言,除非有特殊要求:

从 API 入口,到业务逻辑处理,再到存储,一律使用 utf-8 编码!
从 API 入口,到业务逻辑处理,再到存储,一律使用 utf-8 编码!
从 API 入口,到业务逻辑处理,再到存储,一律使用 utf-8 编码!

如果要支持 emoji 表情,还需额外的处理。

在 HTTP 协议中,Accept-Charset/Content-Type 头部可以指定字符编码方案。

  • Requst 头部: Accept-Charset: utf-8
  • Response 头部: Content-Type: charset=utf-8

序列化

在通信过程中,我们常常需要把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象——这两个功能就是序列化和反序列化。XML/SOAP/JSON 等都是序列化和反序列化协议,其中 XML 多用于 html 类型的应用,而 JSON 成为 html 应用以外的主流序列化和反序列化协议。从个人经验而言,除非特殊需要,建议:

采用 JSON 作为序列化和反序列化协议,对于 html 类型应用,使用 XML。

在 HTTP 协议中,Accept/Content-Type 头部可以指定序列化和反序列化协议。

  • Requst 头部: Accept: application/json
  • Response 头部: Content-Type: application/json

HTTPS

出于安全的因素,此处普及下 HTTPS。HTTPS 由网景公司创建,基于 SSL(Secure Socket Layer) 或 TLS(Transport Layer Security),它作用有两点:一是建立一个信息安全通道,保障报文在各类信道中安全的传输;二是保证网站的真实性。

HTTPS 默认的端口是 443,如果采用默认端口,可以省略 443。例如:

https://example.com/users/john

一般来说,常见的 HTTP Server 都支持 HTTPS 功能,经过简单的配置即可支持 HTTPS。对开发者来说,你需要:

  • 选择支持 HTTPS 协议的 Web Server
  • 知道如何配置证书。

机构颁发的证书一般需要万把块钱购买;你也可以使用 openssl 自己制作证书。如果你对 HTTPS 原理感兴趣,建议了解下 RSA/DH 算法,对称加密/非对称加密,数字签名,还有证书的含义和原理。

推荐阅读

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容