RESTful API 介绍,设计
一:RESTful介绍
在互联网发展过程中,最开始是以html静态网页展示内容,url的表现形式一般为 http://www.example.com/getInfo.html;后来随着需求不断提高以及为了应对这种需求,出现了动态网页技术,在动态网页技术中http请求的url形式一般为 http://www.example.com/getInfo.java?id=1&name=tom ,或者是重写后的url形式http://www.example.com/getInfo/id/1/name/tom 等这种形式。其实这种url的形式是不是有点RESTful的样子了,但是当时还没有这种思想。
在2000年的时候,Roy Thomas Fielding博士在他的博士论文中提出了一种万维网的软件架构风格-REST(全称:Representational State Transfer, 表现层状态转换),目的是便于在不同软件/程序中(例如互联网)中互相传递信息。这种表现层状态转换是基于HTTP协议之上而确定的一组约束和属性,是一种设计提供万维网服务的软件架构风格。
题外话:他也是HTTP协议(1.0版和1.1版)的主要起草者之一、 Apache服务器软件的作者之一、Apache基金会的第一任主席。
符合或兼容于这种架构风格(简称为 REST 或 RESTful)的网络服务,允许客户端发出以统一资源标识符访问和操作网络资源的请求,而与预先定义好的无状态操作集一致化。因此表现层状态转换提供了在互联网络的计算系统之间,彼此资源可交互使用的协作性质。(维基百科)
二:怎么理解RESTful
上面的说明比较学术,其实我抓住几个重点词汇:网络资源,资源标识符号,无状态,表现层,状态转换。
网络资源:就是我们需要获取的数据,比如媒体,文本,图片,视频等等都可以看作是一种资源。
表现层:“资源”是一种信息实体,它可以有多种外在表现形式,比如上面网络资源说明。我们把“资源”具体呈现出来的形式,叫做它的“表现层(Representation)”
资源标识符号:定位资源用的。就像门牌号码可以定位到具体的房间。怎么定位到你需要的资源?就需要用一个资源标识符-URI(统一资源定位符)。每种资源对应一个特定的URI。要获取资源,访问URI就可以,URI是每一个资源的独一无二的地址。就像在网络世界中,IP地址可以定位到独一无二的一台服务器一样。
无状态:我们访问网站时,就是客户端和服务端的一次交互,这就涉及到数据和状态的变化。无状态就是每次访问时,服务端不会保留客户端的信息,每一次客户端访问服务端都是一次全新的访问。HTTP协议就是一种无状态协议。
状态转换:HTTP协议是一个无状态的协议,意思是说所有的状态都是保存在服务端。那么客户端要操作服务端,必须要通过某种手段,让服务端发生“状态转换”(State Transfer)。而这种转换是建立在表现层之上的,就是“表现层状态转换”。
HTTP协议是怎么做到的?就是通过HTTP协议的方法METHOD来操作:GET、PUT、DELETE、POST、PATCH。它们分别对应的四种基本操作时:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,PATCH用来更新部分资源。
三:RESTful 架构
- 有一种资源,每种资源有唯一的URI来标识。
- 对这种资源进行操作,实现状态转换。通常用HTTP的方法来操作。
- 谁来操作谁呢?客户端对服务度资源的操作
具备上面的这几种要素,就是RESTful架构。RESTful是一种设计风格。
四:RESTful API实践
API设计
RESTful API 是基于REST架构设计理念之下利用http协议来描述接口操作接口。
这种API设计主要描述几个部分:
- URL的设计,URL里面当然包含了资源定位符。
- Request参数
- Response 返回数据
URL Request
一个URL设计,需要简单,易懂,看到URL就能知道这个URL的意思,也就是自解释,自描述。
看看http url的设计,分为协议,域名,path,method,参数。
我们应该可以得到启发,它其实就是根据http协议而来。
比如获取雇员url:GET /users, 动作方法+path
- 1.常用的动作方法:
| 动作 | 说明 |
|---|---|
| GET | 读取 Read |
| POST | 新建 Create |
| PUT | 更新 Update |
| PATCH | 更新 Update ,一般为部分更新 |
| DELETE | 删除 Delete |
比如 GET 操作:
GET http://example.com/users 获取所有的员工信息
GET http://example.com/users/23 获取员工id=23的信息
- 2.其他操作:
| 方法/资源 | http://example.com/users | http://example.com/users/23 |
|---|---|---|
| GET | 获取所有的用户信息 | 获取用户id为23的信息 |
| POST | 在所有用户信息中创建/追加一个新的用户 | 在id=23号用户新增信息 |
| PUT | 更新该组用户信息 | 更新id=23号的用户信息 |
| DELETE | 删除所有的用户信息 | 删除id=23号用户信息 |
| PATCH | 更新所有用户部分信息 | 更新id=23号用户信息 |
常见错误:
比如新建一个用户:POST http://example.com/users/22 ,这种形式是错误的,应该用
POST http://example.com/users
- 3. 用复数形式描叙资源
上面的 example.com/users,users复数形式,而且是名词。因为常见的操作是一个集合。所以一般建议用复数形式。不推荐使用 example.com/user/1这种形式,而是推荐使用example.com/users/1 复数这种形式。
错误的表示形式:
GET http://example.com/getusers - 不要使用这种带有get动词的形式
getusers 前面带一个get是多余的,因为http请求的动作就是GET,不用写多余的动作了。如果这样写的话,很容易造成大家理解api不统一。有可能前端和后端人员各自为政,用不同的动词来操作url,造成大家开发理解的不统一。
类似的还有:
- /createUsers
- /updateUserInfo
- /deleteUser?id=2
统一标准:前后端人员统一用http规定的几个动作来进行url相关的操作。不需要在url后面加多余的动作名词。统一用一个标准,这样前后端人员理解起来也能统一思想,便于大家协作开发。
- 4. url的层级
在操作资源的时候,最简单的操作是CURD,但是资源之间相互关联也很常见,资源嵌套也很常见,比如查找id为10的用户所有文章,这个URL如何设计?
一种设计:
GET /users/10/articles
获取article的id=3文章
GET /users/10/articles/3
有人推荐这种方案,这种形式的 URI 一定程度上描述了 user 与 article 之间的一对多关系,但同时,我们就不太能够分清当前端点返回的数据到底是 user 类型还是 article 类型。
这种设计语义不明确,也不利于扩展。
另一种设计,除了第一级,其他级别都用查询字符串表达:
GET /articles?user_id=10
推荐使用这种方案。
比如用户发布文章:GET /atticles?user_id=10&published=true
- 5. 域名的设计
比如我们用独立的api域名:api.example.com。
或者在域名后面加 api,这种形式 example.com/api/users 。
这样有利于以后我们迁移,扩展。
- 6. 版本号
考虑到系统的变化,迭代,和兼容性,在API中引入版本号。
是否在URL中加入版本号,有一个争论。
有的认为不要在URL中加入版本号,可以在请求头信息中加入。因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。
Accept: application/example.com+json; version=3
有的则认为在URL中放入版本号,操作简单。在api变更时候,更容易操作些:
GET api.example.com/v1/users
根据个人来选择,一般推荐在url中写上版本号。
筛选过滤复杂参数-复杂业务场景
对可选的、复杂的参数,使用查询字符串querystring(?)
比如说有一些复杂的业务场景,比如对获取的资源在进行条件筛选,还有排序,分页等等
对于这些场景,RESTful api又怎么设计? 用querystring
- 过滤
比如某一个用户已经发表的文章:
GET /users/10/posts?state=published
GET /users/10/posts?published=true
- 分页
比如用户分页:
GET /users?page=1&page_size=10
在加条件,某一个用户已经发表的文章太多,需要分页:
GET /users/10/posts?published=true&page=2&page_size=10
- 多字段,不同排序
针对多个字段,不同的排序:
搜索用户,并按照注册时间升序、活跃度降序
GET /users?q=key&sort=create_time_asc,liveness_desc
- 显示某些字段
GET /users?fields=id,title,desc;
- 搜索分页排序
github的搜索某一用户分页排序的api:
https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
查询的条件q,如果有需要还有分页page,per_page,排序sort,order。
github某个用户的repo,类型,分页,排序
https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
github的api查看地址:https://api.github.com/
一些非资源的特殊请求
有时API调用并不涉及资源(如计算,翻译或转换)。例:
GET /translate?from=de_DE&to=en_US&text=Hallo
GET /calculate?para2=23¶2=432
在这种情况下,API响应不会返回任何资源。而是执行一个操作并将结果返回给客户端。因此,您应该在URL中使用动词而不是名词,来清楚的区分资源请求和非资源请求。
返回格式和状态码统一
比如返回一个统一json格式:
{
code: 200, //返回的状态码
message: "200 OK", //返回信息简单说明
data: { //返回的数据,如果返回时错误,那么具体内容就是错误信息
}
}
状态码信息
1xx:相关信息
2xx:操作成功
3xx:重定向
4xx:客户端错误
5xx:服务器错误
2xx 状态码
200 表示操作成功,但是不同的方法可以返回更精确的状态码
GET: 200 OK
POST: 201 Created
PUT: 200 OK
PATCH: 200 OK
DELETE: 204 No Content
使用状态码 202 有时候会比 使用状态啊吗 201 是更好的选择,状态码 202 的意思是:服务端已接收到了请求,但是还没有创建任何资源,但结果一切正常
我分享两种特别适合使用 202 Accepted 状态码的业务场景:
- 如果资源是经过位于将来一系列处理流程之后才创建的,比如当某项作业完成时
- 如果资源已经存在,但这是理想状态,因此不应该被识别为一个错误时
3xx 状态码
重定向,需要进一步的操作以完成请求
301 Moved Permanently:永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 Found:已找到。请求的资源已经在其他临时位置找到,并应该在响应头中返回该位置。只有GET和HEAD请求应该重定向到该位置。
304 Not Modified:未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
4xx 状态码
4xx 状态码表示客户端的错误,主要有以下几种:
400 Bad Request:服务器不理解客户端的请求,未做任何处理
401 Unauthorized:用户未提供身份验证凭据,或者没有通过身份验证
403 Forbidden:用户通过了身份验证,但是不具有访问资源所需的权限
404 Not Found:所请求的资源不存在,或不可用
405 Method Not Allowed: 该http方法不被允许。
406 Not Acceptable :GET, 用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 gone : 这个url对应的资源现在不可用。
415 Unsupported Media Type:客户端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是客户端要求返回 XML 格式
429 Too Many Requests : 请求太多 -由于速率限制而拒绝请求时
5xx 状态码
5xx 状态码表示服务端错误
500 Internal Server Error:客户端请求有效,服务器处理时发生了意外
502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应
503 Service Unavailable:服务器无法处理请求,一般用于网站维护状态
505 HTTP Version Not Supported: 服务器不支持,或者拒绝支持在请求中使用的HTTP版本
Hypermedia API
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。
{
"link": {
"rel": "collection https://www.example.com/users",
"href": "https://api.example.com/users",
"title": "List of users",
"type": "application/vnd.yourformat+json"
}
}
上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。
Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。
// https://api.github.com/
{
"current_user_url": "https://api.github.com/user",
"current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
"authorizations_url": "https://api.github.com/authorizations",
"code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
"commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
"emails_url": "https://api.github.com/user/emails",
"emojis_url": "https://api.github.com/emojis",
... ...
}
想获取当前用户信息,访问 https://api.github.com/user,会返回
// https://api.github.com/user
{
"message": "Requires authentication",
"documentation_url": "https://developer.github.com/v3/users/#get-the-authenticated-user"
}
上面代码表示,服务器给出了提示信息,以及文档的网址。
上面介绍的,其实是martin fowler 划分RESTful api4个等级最高的一个等级:
他画的api 4个等级图:

(from: https://martinfowler.com/articles/richardsonMaturityModel.html)
总结URL上的一些设计技巧
- 使用
/来表示资源层级关系 - 使用
?来过滤资源 - 使用
,或;来表示资源同级层关系 - 使用
_或-来表示更友好的URL形式
一些设计也可以参考github的v3: https://developer.github.com/v3/
或者一些其他大厂twitter, https://developer.twitter.com/en,
google等等
参考
- https://zh.wikipedia.org/wiki/REST
- http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
- http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
- https://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html
- https://www.ruanyifeng.com/blog/2011/09/restful.html
- https://blog.florimondmanca.com/restful-api-design-13-best-practices-to-make-your-users-happy
- https://martinfowler.com/articles/richardsonMaturityModel.html
- https://zh.wikipedia.org/wiki/HTTP状态码
- https://www.w3.org/Protocols/rfc2616/rfc2616.html
RESTful API 介绍,设计的更多相关文章
- python 全栈开发,Day95(RESTful API介绍,基于Django实现RESTful API,DRF 序列化)
昨日内容回顾 1. rest framework serializer(序列化)的简单使用 QuerySet([ obj, obj, obj]) --> JSON格式数据 0. 安装和导入: p ...
- RESTful API - 介绍
目录 RESTful API 介绍 RESTful介绍 RESTful API设计指南 API与用户的通信协议 域名 版本(Versioning) 路径(Endpoint) 浏览器请求方式(metho ...
- RESTful API URI 设计: 查询(Query)和标识(Identify)
相关文章:RESTful API URI 设计的一些总结. 问题场景:删除一个资源(Resources),URI 该如何设计? 应用示例:删除名称为 iPhone 6 的产品. 是不是感觉很简单呢?根 ...
- RESTful API URI 设计: 判断资源是否存在?
相关的一篇文章:RESTful API URI 设计的一些总结. 问题场景:判断一个资源(Resources)是否存在,URI 该如何设计? 应用示例:判断 id 为 1 用户下,名称为 window ...
- 好RESTful API的设计原则
说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间,如有人愿意转载请注明出处,谢谢^_^ P ...
- RESTful API的设计原则
好RESTful API的设计原则 说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间, ...
- 好的RESTful API的设计原则
转载自一位大佬 英文原版 Principles of good RESTful API Design Good API design is hard! An API represents a cont ...
- Spring Boot 2.x 编写 RESTful API (一) RESTful API 介绍 & RestController
用Spring Boot编写RESTful API 学习笔记 RESTful API 介绍 REST 是 Representational State Transfer 的缩写 所有的东西都是资源,所 ...
- [Medium翻译]RESTful API权威设计指南-设计更好的API
本文为授权译文.希望查看原文的同学请戳链接:https://hackernoon.com/restful-api-design-step-by-step-guide-2f2c9f9fcdbf 对于我们 ...
- Restful API的设计与实践
Restful这个名称应该很多人都不陌生,但是我发现不少人对Restful存在或多或少的理解偏差,其中不泛比较厉害的程序员,所以有必要为Restful来“正名”. Restful是一种软件架构风格,设 ...
随机推荐
- [转帖]Linux下strace调试系统应用参数总结(附实例操作讲解)
文章目录 一.简介 二.常用参数详解 三.实例详解 3.1 跟踪具体进程 3.2 监控具体程序执行过程 四.其他相关知识链接 一.简介 strace命令是一个集诊断.调试.统计与一体的Linux 用户 ...
- [转帖]Redis各版本特性汇总
redis4 redis5 redis6 redis6.2 重大特性 1.模块系统 2.PSYNC2 3.LFU淘汰策略 4.混合RDB-AOF持久化 5.LAZY FREE延迟释放 6.MEMORY ...
- [转帖]在龙芯3A5000上测试SPEC CPU 2006
https://baijiahao.baidu.com/s?id=1707601012673143593&wfr=spider&for=pc 注:百家号中,一些文本.代码等的排版格式无 ...
- grafana与K8S的下载地址
https://grafana.com/grafana/download/9.1.6 https://dl.k8s.io/v1.15.1/kubernetes-node-linux-amd64.tar ...
- 解决Word等打开嵌入的文件提示 包含有害内容 无法打开的问题
最近打开文件时提示: 从网上找了一下 最简单的解决办法是: 新建一个文件, 输入如下内容 导入注册表 每次打开时不进行 文件有效性的检查即可. 为了省事 我多加了几个版本的 如果是excel 将 w ...
- vue过滤器(filter)的使用
过滤器分全局过滤器和局部过滤器 <div id="app"> <p>电脑价格:{{price | addPriceIcon}}</p> < ...
- word文档删除空白页
记住两个快捷键 CTRL+Backspace Shift+Backspace 鼠标箭头放在空白的页面 按住键盘上的快捷键 就可以成功删除了不要天天看营销号设置什么磅值,全选删除啥的 效果如下
- 大语言模型的预训练[1]:基本概念原理、神经网络的语言模型、Transformer模型原理详解、Bert模型原理介绍
大语言模型的预训练[1]:基本概念原理.神经网络的语言模型.Transformer模型原理详解.Bert模型原理介绍 1.大语言模型的预训练 1.LLM预训练的基本概念 预训练属于迁移学习的范畴.现有 ...
- parser.add_argument()用法——命令行选项、参数和子命令解析器
argparse是一个Python模块:命令行选项.参数和子命令解析器.通过使用这种方法,可以在使用 1.argparse简介: argparse 模块是 Python 内置的一个用于命令项选项与参数 ...
- Flask WTForms 表单插件的使用
在Web应用中,表单处理是一个基本而常见的任务.Python的WTForms库通过提供表单的结构.验证和渲染等功能,简化了表单的处理流程.与此同时,Flask的扩展Flask-WTF更进一步地整合了W ...