参考资料:(1)(2)http://www.ruanyifeng.com/blog/2011/09/restful.html (3) https://www.cnblogs.com/duhuo/p/4245202.html
1, 概念
RESTful 不是一种技术, 而只是一种 API 接口设计规范. 凡是符合该规范的设计, 都可以认为 RESTful 风格的服务.
但是由于各开发人员对 RESTful 的理解存在误区或者业务特殊场景, 因此实际生产的环境中真正完全满足 RESTful 风格的服务并不多, 也没有必要. 但是在实际的应用中, 有 RESTful 标准可以参考, 是十分有必要的.
实际上在工作中, 只要对 API 的接口规范, 命名规则, 返回值, 授权验证等进行一定的约束, 一般的项目 API 只要满足 调试, 足够安全, 风格一致可读性强, 对于调用端没有歧义, 我觉得就够了. 接口是个开放人员看的, 而不是给普通用户去调用的.
2,REST 与 RESTful
2.1 REST(Representational State Transfer)
REST: 即表征状态转移, 是一种互联网软件的架构原则. 如果一个架构符合 REST 原则, 那么它就是 RESTful 架构
要理解 RESTful 就要好好的理解下 REST 的具体含义.
(1)资源(Resource)
REST 的名称 "表现层状态转移" 中的, 表现层其实指的的是 "资源(Resource)" 的表现层.
所谓的资源其实就是, 网络上的一个实体, 或者是网络上的一个具体信息. 可以是一段文本, 一张图片, 一首歌曲, 一种服务. 你可以使用一个 URI(统一资源定位符)指向它. 每种资源对应一个特定的 URI.
所谓的 "上网", 就是与互联网上的一系列 "资源" 互动, 调用他的 URI.
(2)表现层
"资源" 是一种信息实体, 可以多种外在的表现形式. 我们将 "资源" 具体呈现出来的形式, 叫做它的 "表现层"(Representational )
比如: 文本可以用 txt 格式表示, 也可以用 HTML,xml,JSON, 甚至是二进制格式图片可以用 JPG,PNG 表示.
URI 只代表资源的实体位置, 而不代表他的展现形式. 严格的说, 有些网页最后的 ".html" 后缀名, 其实是不必要的, 因为这个后缀名表示格式, 属于 "表现层" 的范畴. 因此它的具体表现形式应该在 HTTP 的请求头信息中用 Accept 和 Content-Type 字段指定.
这两个字段才是对表现层的描述.
(3)状态转化(State Transfer)
访问一个网站, 就代表客户端和服务器的一个互动过程, 在这个过程中势必涉及到数据和状态的变化.
HTTP 协议是一个无状态的协议. 这也就意味着, 所有的状态都存放在服务端. 因此客户要想操作服务器, 就必须以某种手段, 让服务器发生状态转化(state transfer). 而这种转化是建立在表现层之上的, 所以就是 "表现层状态转化".
客户端用到的手段吗, 只能是 HTTP 协议. 具体来说就是, 使用 HTTP 协议里的四个表示操作方式的动词: GET,POST,PUT,DELETE. 他们分别对应四种基本操作. GET(获取资源),POST(新建或者更新资源资源, 新建时不具备幂等性),PUT(更新资源).
DELETE(删除资源)
2.2 RESTful 概述
综上 2.1 所述: 总结一下什么是 RESTful 架构
(1)每一种 URI 代表一种资源
(2)客户端与服务端之间, 具备传递这种资源的表现层
(3)客户端通过 HTTP 的四个动词, 对服务器资源进行操作. 实现表现层的状态转化.
2.3 RESTful 六大原则
也即是 REST 之父阐述的 REST 六大原则
(1)C-S 架构
数据存储在 C-S 端, C 端只需要使用就可以. 两端彻底分离的好处使得 C 端代码的可移植性变强, Server 端额可拓展性变强. 两端独立开发, 互不干扰.
(2)无状态
http 请求本身就是无状态的, 基于 C-S 架构, 客户端的每一次请求带有充分的信息能够让服务端识别. 请求所需的一些信息都包含在 URL 的查询参数, header,div, 服务端能够根据请求的各种参数, 无需保存客户端的状态, 将响应正确返回给客户端. 无状态的特征大大提高的服务端的健壮性和可拓展性.
当然这总无状态性的约束也是有缺点的, 客户端的每一次请求都必须带上相同重复的信息确定自己的身份和状态(这也是必须的), 造成传输数据的冗余性, 但这种确定对于性能和使用来说, 几乎是忽略不计的.,
(3)统一的接口
这个才是 REST 架构的核心, 统一的接口对于 RESTful 服务非常重要. 客户端只需要关注实现接口就可以, 接口的可读性加强, 使用人员方便调用.
(4)一致的数据格式
服务端返回的数据格式要么是 xml, 要么是 JSON(获取数据), 或者直接返回状态码, 有兴趣的可以看看博客园的开放平台的操作数据的 API,post,put,patch 都是返回的一个状态码 .
自我描述的信息, 每项数据应该是可以自我描述的, 方便代码去处理和解析其中的内容. 比如通过 HTTP 返回的数据里面有 [MIME type ]信息, 我们从 MIME type 里面可以知道数据的具体格式, 是图片, 视频还是 JSON, 客户端通过 div 内容, 查询串参数, 请求头和 URI(资源名称)来传送状态. 服务端通过 div 内容, 响应码和响应头传送状态给客户端. 这项技术被称为超媒体(或超文本链接).
除了上述内容外, HATEOS 也意味着, 必要的时候链接也可被包含在返回的 div(或头部)中, 以提供 URI 来检索对象本身或关联对象. 下文将对此进行更详细的阐述.
如请求一条微博信息, 服务端响应信息应该包含这条微博相关的其他 URL, 客户端可以进一步利用这些 URL 发起请求获取感兴趣的信息, 再如分页可以从第一页的返回数据中获取下一页的 URT 也是基于这个原理.
(5)可缓存
在万维网上, 客户端可以缓存页面的响应内容. 因此响应都应隐式或显式的定义为可缓存的, 若不可缓存则要避免客户端在多次请求后用旧数据或脏数据来响应. 管理得当的缓存会部分地或完全地除去客户端和服务端之间的交互, 进一步改善性能和延展性.
(6)按需编码, 可定制代码(可选)
服务端可选择临时给客户端下发一些功能代码让客户端来执行, 从而定制和扩展客户端的某些功能. 比如服务端可以返回一些 JavaScript 代码让客户端执行, 去实现某些特定的功能. 提示: REST 架构中的设计准则中, 只有按需编码为可选项. 如果某个服务违反了其他任意一项准则, 严格意思上不能称之为 RESTful 风格.
2.4 RESTful 的最佳实践
(1)版本
如 GitHub 开放平台 http://developer.github.com/v3/
就是将版本放在 url, 简洁明了, 这个只有用了才知道, 一般的项目加版本 v1,v2,v3? 好吧, 这个加版本估计只有大公司大项目才会去使用. 但是一般是将请求版本号放在 header 里面
(2)参数命名规范
query 参数可以采用驼峰命名方法, 也可以采用下划线的命名方式.
例如:
http://example.com/api/users/today_login 获取今天登陆的用户
http://example.com/api/users/today_login&sort=login_desc 获取今天登陆的用户, 登陆时间降序排列
(3)url 命名规范
API 命名应该采用约定俗成的方式, 保持简洁明了. 在 RESTful 架构中, 每个 url 代表一种资源所以 url 中不能有动词, 只能有名词, 并且名词中也应该使用复数. 实现者应使用相应的 Http 动词 GET,POST,PUT,PATCH,DELETE,HEAD 来操作这些资源即可
不规范的的 url, 冗余没有意义, 形式不固定, 不同的开发者还需要了解文档才能调用.
但是一般实践中, 像管理后台经常通过在 url 末尾加上 add/delete/update/list 方式来区分接口(这样也有一定的可读性)
如:
规范化后的 RESTful 风格的 url, 形式固定, 可读性强. 根据名词以及 http 请求动词就可以判断与操作这些资源
(4)返回的数据格式统一
对于合法的请求应该统一返回数据格式, 这里演示的是 JSON
code-- 包含一个整数类型的 HTTP 响应状态码.
status-- 包含文本:"success","fail" 或 "error".HTTP 状态响应码在 500-599 之间为 "fail", 在 400-499 之间为 "error", 其它均为 "success"(例如: 响应状态码为 1XX,2XX 和 3XX). 这个根据实际情况其实是可要可不要的.
message-- 当状态值为 "fail" 和 "error" 时有效, 用于显示错误信息. 参照国际化 (il8n) 标准, 它可以包含信息号或者编码, 可以只包含其中一个, 或者同时包含并用分隔符隔开.
data-- 包含响应的 div. 当状态值为 "fail" 或 "error" 时, data 仅包含错误原因或异常名称, 或者 null 也是可以的
返回成功的响应 JSON 格式
返回失败的响应 JSON 格式
当然, 也可以根据自己业务的实际情况进行扩展, 笔者所在的业务部门, 统一的返回格式如下:
- {
- errno:0,// 错误码, 0 为成功, 其他为失败(接口报错, 服务不可用)
- msg:**,// 错误提示信息, 一般不展示给用户. 前端可以根据这个信息做相应的逻辑处理
- showMsg:**, // 错误展示信息, 一般是用来展示给用户的
errTag:SUCCESS 或者 FAIL,// 一般用来标记状态是否成功
- data:**,// 返回数据, 一般放在这个 data 字段里. 若无数据返回, 则置为 null 值
- }
这个可以在后端代码中进行相应的封装, 在 controller 层返回的时候调用实例化包装对象即可.
(5)HTTP 状态码
一般通过 Response 响应头中的状态码也可以知道自己的请求是否成功.(4)中统一返回的状态码是对 HTTP 状态码的一个细分.
常见的 HTTP 状态码如下:
1** 请求未成功
2** 请求成功, 表示成功处理了请求的状态代码.
3** 请求被重定向, 表示要完成请求, 需要进一步操作. 通常, 这些状态代码用来重定向.
4** 请求错误这些状态代码表示请求可能出错, 妨碍了服务器的处理. 404: 页面不存在或已经删除. 403: 资源不可用, 服务器理解客户的请求, 但拒绝处理它, 通常由于服务器上文件或目录的权限设置导致的 web 访问错误. 405: 表示不允许使用该类型方法进行请求.
5**(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误. 这些错误可能是服务器本身的错误, 而不是请求出错. 502 :Bad Gateway 是指错误网关 https://baike.baidu.com/item/网关 , 无效网关. 它通常并不意味着上游服务器已关闭(无响应网关 / 代理) , 而是上游服务器和网关 / 代理使用不一致的协议交换数据. 504 : 错误代表网关超时 (Gateway timeout), 是指服务器作为网关或代理, 但是没有及时从上游服务器收到请求.
(6) 合理使用 query parameter
在请求数据时, 客户端经常会对数据进行过滤和分页等要求, 而这些参数推荐采用 HTTP Query Parameter 的方式实现
关于分页, 看看博客园开放平台分页获取精华区博文列表
http://api.cnblogs.com/api/blogposts/@picked?pageIndex={pageIndex}&pageSize={pageSize}
返回示例:
- [
- {
- "Id": 1,
- "Title": "sample string 2",
- "Url": "sample string 3",
- "Description": "sample string 4",
- "Author": "sample string 5",
- "BlogApp": "sample string 6",
- "Avatar": "sample string 7",
- "PostDate": "2017-06-25T20:13:38.892135+08:00",
- "ViewCount": 9,
- "CommentCount": 10,
- "DiggCount": 11
- },
- {
- "Id": 1,
- "Title": "sample string 2",
- "Url": "sample string 3",
- "Description": "sample string 4",
- "Author": "sample string 5",
- "BlogApp": "sample string 6",
- "Avatar": "sample string 7",
- "PostDate": "2017-06-25T20:13:38.892135+08:00",
- "ViewCount": 9,
- "CommentCount": 10,
- "DiggCount": 11
- }
- ]
来源: http://www.bubuko.com/infodetail-3281580.html