REST
1、什么是REST?
“REST不是一种协议,也不是一种文件格式,更不是一种开发框架。它是一系列设计约束的集合。” --是一组架构约束条件和原则。
REST描述的是在网络中client和server的一种交互形式;
REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口)
REST(REpresentational State Transfer)–表现层状态转移–是一种软件架构风格。
Resource Representational State Transfer:资源在网络中以某种表现形式进行状态转移。
通过URL地址拼接上HTTP的动词(GET,POST,PUT,DELETE)来操作资源,进行增删改查。
URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作。
2、REST用来干什么?
API(Application Programming Interface,应用程序编程接口),通俗点的解释就像一家公司,比如阿里、腾讯、百度等,它们可以提供一个API,然后码农可以编写软件与BAT等公司提供的API对接或交互。比如可以用手机的其他软件分享内容到微信/微信朋友圈或新浪微博等,这些软件就是与微信或微博API进行了交互。
REST就是一种建立API时需要遵守的一种规则/风格。-设计RESTful API
REST式的web服务是一种ROA(Resource-Oriented Architecture,面向资源的架构)。
Web 是分布式信息系统,为超文本文件和其它对象(资源)提供访问入口。
资源是Web架构的关键点,需要3个操作:识别 identify、表示 represent、 交互 interaction;通过这三个操作,又可以引出三个概念:统一资源标识符URI–识别资源、representation(html,xml,图片,视频等)–表示资源、通过协议(HTTP,FTP等)与资源进行交互。
REST选择通过HTTP协议和URI,利用client/server model对资源进行CRUD操作。
3、REST的基本概念:
五个关键词:
• 资源(Resource)• 资源的表述(Representation)• 状态转移(State Transfer)
• 统一接口(Uniform Interface)• 超媒体驱动(Hypermedia Driven)
- 服务器生成包含状态转移的表征数据,用来响应客户端对于一个资源的请求;
- 客户端借助这份表征数据,记录了当前的应用状态以及对应可转移状态的方式。
当然,为了要实现这一系列的功能,一个不可或缺的东西就是超文本(hypertext)或者说超媒体类型(hypermedia type)。这绝对不是一个简简单单的媒体类型(例如,JSON属性列表)可以做到的。
REST基础概念:
1)Resource资源 – 网络上所有事物都被抽象为资源;
2)Representation表现层 – 同一个资源具有多种表现形式(xml,json等);
3)每个资源都有一个唯一的资源标识符URI,对资源的各种操作不会改变资源标识符;
State Transfer 状态转化–客户端想要操作服务器,必须通过某种手段,让服务器发生表现层的状态转化。
4)使用统一的接口。处理资源使用POST,GET,PUT,DELETE操作类似创建,读取,更新和删除(CRUD)操作;
5)所有的操作都是无状态的(基于http协议)。每个请求是一个独立的请求。从客户端到服务器的每个请求都必须包含所有必要的信息,以便于理解。
REST本身不是架构,只是一种架构风格,理解它的时候要参考这个架构风格出现的环境所施加的约束条件。
REST的目的是“建立十年内不会过时的软件系统架构",所以它具备三个特点:
1. 状态无关 —— 确保系统的横向拓展能力
2. 超文本驱动,Fielding的原话是”hypertext-driven" —— 确保系统的演化能力
3. 对 resource 相关的模型建立统一的原语,例如:uri、http的method定义等 —— 确保系统能够接纳多样而又标准的客户端
从另外一个角度看,第一条保证服务端演化,第三条保证客户端演化,第二条保证应用本身的演化,这实在是一个极具抽象能力的方案。
4、REST操作
一般资源操作只有新增、删除、查询、更新,对应HTTP协议中四类请求:POST、DELETE、GET、PUT。
其中,DELETE、GET、PUT操作是幂等的。
【分布式系统中的幂等性】、【幂等性问题】【面试题:怎么实现接口的幂等性】
幂等性:
–使公式:f(x)=f(f(x)) 能够成立的数学性质
对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。
举个最简单的例子,那就是支付,用户购买商品使用约支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...
5、什么是RESTful架构:
如果一个架构符合REST原则,就称它为RESTful架构。
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递某资源的一个表现形式;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
ps:
- URI不应该包含动词,动词应该放在HTTP协议中;
—如果某些动作使用HTTP动词表示不了,就应该把动作做成一种资源 - URI中不应该加入版本号
—不同的版本,可以理解成同一个资源的不同表现形式,应该采用同一个URI。
—版本号可以在HTTP请求头信息的Accept字段中进行区分。
RESTful
看Url就知道要什么
看http method就知道干什么
看http status code就知道结果如何
1、为什么使用 RESTful 结构?/ RESTful结构的好处是什么?
1.JSP技术可以让我们在页面中嵌入Java代码,但是这样的技术实际上限制了我们的开发效率,因为需要我们Java工程师将html转换为jsp页面,并写一些脚本代码,或者前端代码。这样会严重限制我们的开发效率,也不能让我们的java工程师专注于业务功能的开发,所以目前越来越多的互联网公司开始实行前后端分离。
2.近年随着移动互联网的发展,各种类型的Client层出不穷,RESTful可以通过一套统一的接口为Web,iOS和Android提供服务。另外对于广大平台来说,比如微博开放平台,微信开放平台等,它们不需要有显式的前端,只需要一套提供服务的接口,RESTful无疑是最好的选择。
RESTful架构如下:
RESTful 结构的约束 | 描述 | 好处 |
---|---|---|
1、客户端-服务器(Client-Server)分离 | 客户端和服务器是分离的 | 1、提高用户界面的便携性(操作简单)2、通过简化服务器提高可伸缩性(高性能,低成本)3、允许服务器和客户端分别进行改进和优化 |
2、无状态(Stateless) | 从客户端的每个请求都要包含服务器所需要的所有信息 | 1、提高可见性(可以单独考虑每个请求)2、提高可靠性(更容易从局部故障中修复)3、提高可扩展性(降低了服务器资源使用) |
3、缓存(Cachable) | 服务器返回信息必须标记是否可以缓存:如果缓存,客户端可能会重用之前的信息发送请求 | 1、减少交互次数 2、减少交互的平均延迟 |
4、分层系统(Layered System) | 系统组件不需要知道与之交流组件之外的事情。封装服务,引入中间层。 | 限制了系统的复杂性、提高可扩展性 |
5、统一接口(Uniform Interface) | 提高交互的可见性、鼓励单独改善组件 | |
6、支持按需代码(Code-On-Demand可选) | 提高可扩展性 |
比较了三种架构风格之间的差别之后,从面向实用的角度来看,REST 架构风格可以为 Web 开发者带来三方面的利益:
-
简单性
采用 REST 架构风格,对于开发、测试、运维人员来说,都会更简单。可以充分利用大量 HTTP 服务器端和客户端开发库、Web 功能测试 / 性能测试工具、HTTP 缓存、HTTP 代理服务器、防火墙。这些开发库和基础设施早已成为了日常用品,不需要什么火箭科技(例如神奇昂贵的应用服务器、中间件)就能解决大多数可伸缩性方面的问题。 -
可伸缩性
充分利用好通信链各个位置的 HTTP 缓存组件,可以带来更好的可伸缩性。其实很多时候,在 Web 前端做性能优化,产生的效果不亚于仅仅在服务器端做性能优化,但是 HTTP 协议层面的缓存常常被一些资深的架构师完全忽略掉。 -
松耦合
统一接口 + 超文本驱动,带来了最大限度的松耦合。允许服务器端和客户端程序在很大范围内,相对独立地进化。对于设计面向企业内网的 API 来说,松耦合并不是一个很重要的设计关注点。但是对于设计面向互联网的 API 来说,松耦合变成了一个必选项,不仅在设计时应该关注,而且应该放在最优先位置。
一些对实时性要求很高的应用,REST 的表现不如 DO 和 RPC。所以需要针对具体的运行环境来具体问题具体分析。
2、之前及RESTful 用法对比:
之前的用法:
1)http://127.0.0.1/user/query/1 (GET请求方式,根据用户id查询用户数据);
2)http://127.0.0.1/user/save (POST请求方式,保存用户);
3)http://127.0.0.1/user/update (POST请求方式,修改用户信息);
4)http://127.0.0.1/user/delete/1 (GET/POST请求方式,根据用户id删除用户信息)
RESTful的用法:
1)http://127.0.0.1/user/1 (GET请求方式,根据用户id查询用户数据);
2)http://127.0.0.1/user (POST请求方式,保存用户);
3)http://127.0.0.1/user (PUT请求方式,修改用户信息);
4)http://127.0.0.1/user (DELETE请求方式,删除用户信息)
3、RESTful API 设计指南
0、some tricks:
- GET及HEAD必须是安全的。
- URL是区分大小写的;/Posts和/posts是不同的两个URL,可以指向不同的资源
- 建议URL以/作为结尾;/posts/和/posts也是两个不同的URL,可以对应不同的行为和资源
- 当URL中某一个片段(segment)由多个单词组成时,建议使用 - 来隔断单词,而不是使用 _
1、域名-URL root
应该尽量将API部署在专用域名之下
https://api.example.com
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
https://example.org/api/
2、版本号-API versioning
规范的API应该包含版本信息,在RESTful API中,最简单的包含版本的方法是将版本信息放到url中,如:
https://api.example.com/v1/
也可以使用HTTP header中的accept来传递版本信息,这也是GitHub API 采取的策略。
3、路径设计
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表名对应,一般来说,数据库中的表都是同种记录的”集合”(collection),所以API中的名词也应该使用复数。
资源的URI格式:协议://域名/路径,它只是URL的子集,表征一个资源实体。比如,http://a.com/users/1。
举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
Server提供的RESTful API中,URL中只使用名词来指定资源,原则上不使用动词。“资源”是REST架构或者说整个网络处理的核心。比如:
http://api.qc.com/v1/newsfeed: 获取某人的新鲜; http://api.qc.com/v1/friends: 获取某人的好友列表;http://api.qc.com/v1/profile: 获取某人的详细信息;
4、HTTP动词
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面五个(括号里是对应的SQL命令)。
用HTTP协议里的动词来实现资源的添加,修改,删除等操作。即通过HTTP动词来实现资源的状态转化
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
下面是一些例子。
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
又比如:
DELETE http://api.qc.com/v1/friends: 删除某人的好友 (在http parameter指定好友id)
POST http://api.qc.com/v1/friends: 添加好友
UPDATE http://api.qc.com/v1/profile: 更新个人资料
禁止使用: GET http://api.qc.com/v1/deleteFri
5、过滤信息(Filtering)
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数。
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
6、状态码(Status Codes)
用 HTTP Status Code传递Server的状态信息。
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
状态码的完全列表参见这里。
7、错误处理(Error handling)
出错,需要返回错误原因.
如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
{error: “Invalid API key”}
8、接口即文档
Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。这种设计被称为HATEOAS。
{
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}",
emails_url: "https://api.github.com/user/emails",
emojis_url: "https://api.github.com/emojis",
events_url: "https://api.github.com/events",
feeds_url: "https://api.github.com/feeds",
followers_url: "https://api.github.com/user/followers",
following_url: "https://api.github.com/user/following{/target}",
gists_url: "https://api.github.com/gists{/gist_id}",
hub_url: "https://api.github.com/hub",
issue_search_url: "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
issues_url: "https://api.github.com/issues",
keys_url: "https://api.github.com/user/keys",
notifications_url: "https://api.github.com/notifications",
organization_repositories_url: "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
organization_url: "https://api.github.com/orgs/{org}",
public_gists_url: "https://api.github.com/gists/public",
rate_limit_url: "https://api.github.com/rate_limit",
repository_url: "https://api.github.com/repos/{owner}/{repo}",
repository_search_url: "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
current_user_repositories_url: "https://api.github.com/user/repos{?type,page,per_page,sort}",
starred_url: "https://api.github.com/user/starred{/owner}{/repo}",
starred_gists_url: "https://api.github.com/gists/starred",
team_url: "https://api.github.com/teams",
user_url: "https://api.github.com/users/{user}",
user_organizations_url: "https://api.github.com/user/orgs",
user_repositories_url: "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
user_search_url: "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
}
9、其他
(1)API的身份认证应该使用OAuth 2.0框架。
(2)服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
由于RESTful风格的服务是无状态的,认证机制尤为重要。例如上文提到的员工工资,这应该是一个隐私资源,只有员工本人或其他少数有权限的人有资格看到,如果不通过权限认证机制对资源做一层限制,那么所有资源都以公开方式暴露出来,这是不合理的,也是很危险的。
认证机制解决的问题是,确定访问资源的用户是谁;权限机制解决的问题是,确定用户是否被许可使用、修改、删除或创建资源。权限机制通常与服务的业务逻辑绑定,因此权限机制需要在每个系统内部定制,而认证机制基本上是通用的,常用的认证机制包括 session auth(即通过用户名密码登录),basic auth,token auth和OAuth,服务开发中常用的认证机制为后三者。
From:知乎形象例子
单机软件的文件路径 = REST软件的Url
单机软件的打开操作 = REST软件HTTP的GET
单机软件的保存操作 = REST软件HTTP的POST
那么非REST架构软件(SOAP)和REST架构软件之间的的区别是啥呢?
其实区别就是在于对应的取资源的方式,SOAP用的是自己规定好的格式,而REST用的是大统一的Url格式。所以你想从SOAP软件中存取操作,必须事先知道它的读写格式;而REST,你只需要有一个浏览器就可以了。