理解 RESTful API 面向资源 幂等性

如何理解RESTful的幂等性

 我来答

分享

举报浏览 3612 次

1个回答

#再见,2018!# 2018要结束了,你还有哪些心愿没完成??

最佳答案

热心网友 
2017-07-02

等幂性(Idempotence)
等幂性简单点说就是一次请求和多次请求,资源的状态是一样。比如GET和HEAD,不论你请求多少次,资源还是在那里。请注意,DELETE和PUT也是等幂的,以为对同一个资源删除一次或者多次,结果是一样的,就是资源被删除了,不存在了。为什么说PUT也是等幂的?当你PUT一个新资源的时候,资源被创建,再次PUT这个URI的时候,资源还是没变。当你PUT一个存在的资源时,更新了资源,再次PUT的时候,还是更新成这个样子。在PUT更新的时候,不能做相对的更新(依赖资源现在的状态),比如每次对一个数加1,这样资源状态就会变化。应该每次更新成某个数,比如把某个数变成4,则无论多少次PUT,值都是4,这样就是等幂了。
我们设计Restful WS的时候,GET,HEAD, PUT, DELETE一定要设计成等幂的。由于网络是不可靠的,安全性和等幂性就显得特别重要。如果一次请求,服务器收到处理以后,客户端没有收到相应,客户端会再次请求,如果没有等幂性保障,就会发生意想不到的问题。
POST是不安全也不等幂的,还是拿weblog的例子,如果两次POST相同的博文,则会产生两个资源,URI可能是这样/weblogs/myweblog/entries/1和/weblogs/myweblog/entries/2,尽管他们的内容是一摸一样的。

https://zhidao.baidu.com/question/373737226952238764.html

随笔- 63  文章- 0  评论- 25 

RESTful 个人理解总结

一.什么是RESTful 面向资源

简单的说:RESTful是一种架构的规范与约束、原则,符合这种规范的架构就是RESTful架构。

先看REST是什么意思,英文Representational state transfer 表述性状态转移 其实就是对 资源 的表述性状态转移。

资源的地址 在web中就是URL (统一资源标识符)

资源是REST系统的核心概念。 所有的设计都是以资源为中心

结合项目怎么识别资源

1.商品加入购物车 购物车

2.提交订单 订单

3.创建用户 用户

围绕资源进行 添加,获取,修改,删除,以及对符合特定条件的资源进行列表操作 。针对资源设计接口

二.关于规范与约束有哪些?

RESTful 架构的核心规范与约束:统一接口

分为四个子约束:

1.每个资源都拥有一个资源标识,每个资源的资源标识可以用来唯一地标明该资源

2.消息的自描述性

3.资源的自描述性。

4.HATEOAS Hypermedia As The Engine Of Application State(超媒体作为应用状态引擎)

即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息,如到底是向哪个URL发送请求等。也就是说,一个典型的REST服务不需要额外的文档标示通过哪些URL访问特定类型的资源,而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作

目的:实现客户端无需借助任何文档即能调用到所有的服务器资源

 

三、资源的URL设计

1.通过URL来表示资源

资源分为主资源与子资源

因为主资源是一类独立的资源 所以主资源应直接放在相对路径下:例如

若要表示主资源的实例:如果实例的ID=1,则这样表示: /goods/1

子资源:

一个实例的子资源可能是一个集合也可能是一个单一的子资源

子资源为图片集合:/goods/1/pictures

子资源为商品折扣的单子子资源:/goods/1/discount

2.单数 vs. 复数

获取用户1的信息,哪种方式更符合RESTful?

/api/users/1

/api/user/1

3.相对路径 vs. 请求参数

极光的RESTful API:

获取用户信息 GET /v1/users/{username} 参数放在路径中

VS

获取用户信息 GET /v1/users?username=xxxxx 拼接的方式

获取应用管理员列表 GET /v1/admins?start={start}&count={count} ?后拼接参数的方式:这种方式一般作为过滤资源

4.使用合适的动词 get delete put post

选择请求接口的方式: get delete

PUT 在服务器更新资源(客户端提供改变后的完整资源)。

POST 在服务器新建一个资源

5.使用标准的状态码

GET

  • 安全且幂等
  • 获取表示
  • 变更时获取表示(缓存)
  • 200(OK) - 表示已在响应中发出
  • 204(无内容) - 资源有空表示
  • 301(Moved Permanently) - 资源的URI已被更新
  • 303(See Other) - 其他(如,负载均衡)
  • 304(not modified)- 资源未更改(缓存)
  • 400 (bad request)- 指代坏请求(如,参数错误)
  • 404 (not found)- 资源不存在
  • 406 (not acceptable)- 服务端不支持所需表示
  • 500 (internal server error)- 通用错误响应
  • 503 (Service Unavailable)- 服务端当前无法处理请求
  • POST

    • 不安全且不幂等
    • 使用服务端管理的(自动产生)的实例号创建资源
    • 创建子资源
    • 部分更新资源
    • 如果没有被修改,则不过更新资源(乐观锁)
    • 200(OK)- 如果现有资源已被更改
    • 201(created)- 如果新资源被创建
    • 202(accepted)- 已接受处理请求但尚未完成(异步处理)
    • 301(Moved Permanently)- 资源的URI被更新
    • 303(See Other)- 其他(如,负载均衡)
    • 400(bad request)- 指代坏请求
    • 404 (not found)- 资源不存在
    • 406 (not acceptable)- 服务端不支持所需表示
    • 409 (conflict)- 通用冲突
    • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
    • 415 (unsupported media type)- 接受到的表示不受支持
    • 500 (internal server error)- 通用错误响应
    • 503 (Service Unavailable)- 服务当前无法处理请求

    PUT

    • 不安全但幂等
    • 用客户端管理的实例号创建一个资源
    • 通过替换的方式更新资源
    • 如果未被修改,则更新资源(乐观锁)
    • 200 (OK)- 如果已存在资源被更改
    • 201 (created)- 如果新资源被创建
    • 301(Moved Permanently)- 资源的URI已更改
    • 303 (See Other)- 其他(如,负载均衡)
    • 400 (bad request)- 指代坏请求
    • 404 (not found)- 资源不存在
    • 406 (not acceptable)- 服务端不支持所需表示
    • 409 (conflict)- 通用冲突
    • 412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
    • 415 (unsupported media type)- 接受到的表示不受支持
    • 500 (internal server error)- 通用错误响应
    • 503 (Service Unavailable)- 服务当前无法处理请求

    DELETE

    • 不安全但幂等
    • 删除资源
    • 200 (OK)- 资源已被删除
    • 301 (Moved Permanently)- 资源的URI已更改
    • 303 (See Other)- 其他,如负载均衡
    • 400 (bad request)- 指代坏请求
    • 404 (not found)- 资源不存在
    • 409 (conflict)- 通用冲突
    • 500 (internal server error)- 通用错误响应
    • 503 (Service Unavailable)- 服务端当前无法处理请求
  • 6.选择适当的表示结构

    json   xml

    7. 版本控制

    例如:

hello world!!!

http://www.cnblogs.com/wang-yaz/p/9237981.html

如何理解RESTful的幂等性

2017年02月18日 23:31:57 garfielder007 阅读数:5554

理解RESTful的幂等性,并且设计符合幂等规范的高质量RESTful API。

怎么理解幂等性

HTTP幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的。

还是以之前的博文的例子为例。

 
  1. GET /tickets # 获取ticket列表

  2. GET /tickets/12 # 查看某个具体的ticket

  3. POST /tickets # 新建一个ticket

  4. PUT /tickets/12 # 更新ticket 12

  5. PATCH /tickets/12 # 更新ticket 12

  6. DELETE /tickets/12 # 删除ticekt 12

HTTP GET方法

HTTP GET方法,用于获取资源,不管调用多少次接口,结果都不会改变,所以是幂等的。

 
  1. GET /tickets # 获取ticket列表

  2. GET /tickets/12 # 查看某个具体的ticket

只是查询数据,不会影响到资源的变化,因此我们认为它幂等。

值得注意,幂等性指的是作用于结果而非资源本身。怎么理解呢?例如,这个HTTP GET方法可能会每次得到不同的返回内容,但并不影响资源。

可能你会问有这种情况么?当然有咯。例如,我们有一个接口获取当前时间,我们就应该设计成

GET     /service_time # 获取服务器当前时间

它本身不会对资源本身产生影响,因此满足幂等性。

HTTP POST方法

HTTP POST方法是一个非幂等方法,因为调用多次,都将产生新的资源。

POST    /tickets       # 新建一个ticket

因为它会对资源本身产生影响,每次调用都会有新的资源产生,因此不满足幂等性。

HTTP PUT方法

HTTP PUT方法是不是幂等的呢?我们来看下

PUT     /tickets/12    # 更新ticket 12

因为它直接把实体部分的数据替换到服务器的资源,我们多次调用它,只会产生一次影响,但是有相同结果的 HTTP 方法,所以满足幂等性。

HTTP PATCH方法

HTTP PATCH方法是非幂等的。HTTP POST方法和HTTP PUT方法可能比较好理解,但是HTTP PATCH方法只是更新部分资源,怎么是非幂等的呢?

因为,PATCH提供的实体则需要根据程序或其它协议的定义,解析后在服务器上执行,以此来修改服务器上的资源。换句话说,PATCH请求是会执行某个程序的,如果重复提交,程序可能执行多次,对服务器上的资源就可能造成额外的影响,这就可以解释它为什么是非幂等的了。

可能你还不能理解这点。我们举个例子

PATCH   /tickets/12    # 更新ticket 12

此时,我们服务端对方法的处理是,当调用一次方法,更新部分字段,将这条ticket记录的操作记录加一,这次,每次调用的资源是不是变了呢,所以它是有可能是非幂等的操作。

HTTP DELETE方法

HTTP DELETE方法用于删除资源,会将资源删除。

DELETE  /tickets/12    # 删除ticekt 12

调用一次和多次对资源产生影响是相同的,所以也满足幂等性。

如何设计符合幂等性的高质量RESTful API

HTTP GET方法 vs HTTP POST方法

也许,你会想起一个面试题。HTTP请求的GET与POST方式有什么区别?你可能会回答到:GET方式通过URL提交数据,数据在URL中可以看到;POST方式,数据放置在HTML HEADER内提交。但是,我们现在从RESTful的资源角度来看待问题,HTTP GET方法是幂等的,所以它适合作为查询操作,HTTP POST方法是非幂等的,所以用来表示新增操作。

但是,也有例外,我们有的时候可能需要把查询方法改造成HTTP POST方法。比如,超长(1k)的GET URL使用POST方法来替代,因为GET受到URL长度的限制。虽然,它不符合幂等性,但是它是一种折中的方案。

HTTP POST方法 vs HTTP PUT方法

对于HTTP POST方法和TTP PUT方法,我们一般的理解是POST表示创建资源,PUT表示更新资源。当然,这个是正确的理解。

但是,实际上,两个方法都用于创建资源,更为本质的差别是在幂等性。HTTP POST方法是非幂等,所以用来表示创建资源,HTTP PUT方法是幂等的,因此表示更新资源更加贴切。

HTTP PUT方法 vs HTTP PATCH方法

此时,你看会有另外一个问题。HTTP PUT方法和HTTP PATCH方法,都是用来表述更新资源,它们之间有什么区别呢?我们一般的理解是PUT表示更新全部资源,PATCH表示更新部分资源。首先,这个是我们遵守的第一准则。根据上面的描述,PATCH方法是非幂等的,因此我们在设计我们服务端的RESTful API的时候,也需要考虑。如果,我们想要明确的告诉调用者我们的资源是幂等的,我的设计更倾向于使用HTTP PUT方法。

from:http://blog.720ui.com/2016/restful_idempotent/ 

http://www.tuicool.com/articles/E7zeeii

https://blog.csdn.net/garfielder007/article/details/55684420

RESTful API 最佳实践

用户2214491发表于java一日一条订阅

119

在这篇文章中:

在参考了GitHub API设计和大量博客文章后总结了一下RESTful API的设计,分享如下。想要更好的理解RESTful API首先需要理解如下概念:

REST:REST(Representational State Transfer)这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的,翻译成中文大意为表现层状态传输。由于他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席,所以REST原则迅速流行起来。当一个软件架构符合REST原则,我们称之为RESTful架构。说了这么多,我们为什么要使用RESTful架构?使用RESTful架构有什么好处?因为按照RESTful架构可以充分的利用HTTP协议带给我们的各种功能,算是对HTTP协议使用的最佳实践,还有一点就是可以使软件架构设计更加清晰,可维护性更好,但是并不是所有情况都需要完全遵守REST原则,毕竟实际情况远远比REST原则所定义的更加复杂,下面会详细介绍。

幂等性:幂等性(Idempotence)本身是一个数学概念,在HTTP/1.1规范中幂等性的定义是:

Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

翻译过来大意就是如果方法调用一次和多次产生额外的效果是相同的,它就具有幂等性。

例子:在HTTP中使用GET方法通常用于从服务器获取资源,无论调用多少次产生的额外效果都是从服务器获取资源,所以GET具有幂等性;而POST方法通常用于提交数据在服务器上创建一个资源,由于最终创建的结果每次都是不同的,所以POST不具有幂等性;但是PUT方法却是幂等的,因为每次调用产生的效果都是对资源进行更新。

安全方法:安全方法是指不修改资源的 HTTP 方法。譬如,当使用 GET 或者 HEAD 作为资源 URL,都必须不去改变资源。然而,这并不全准确。意思是:它不改变资源的 表示形式。对于安全方法,它仍然可能改变服务器上的内容或资源,但这必须不导致不同的表现形式。

有关HTTP常用方法幂等性和安全性如下:

RESTful API设计规则:

1. URI

  • 应该将API部署在专用域名之下:https://api.example.com
  • 不用大写
  • 用中杠-不用下杠_;
  • 参数列表要encode;
  • URI中不应该出现动词,动词应该使用HTTP方法表示,但是如果无法表示,也可使用动词,例如:search没有对应的HTTP方法,可以在路径中使用search,更加直观;
  • URI中的名词表示资源集合,使用复数形式;
  • 虽然/在URI中表达层级,但是避免为了追求REST导致层级过深,适当使用参数表示。 GET /comments/tid/tid=1&page=1

2. Request:通过标准HTTP方法对资源CRUD

  • GET:查询资源 GET /comments //获取所有评论 GET /comments/tid/1 //获取文章tid为1的所有评论
  • POST:创建资源 POST /comments/tid/1 //为tid为1的文章创建评论
  • PUT:更新资源 PUT /comments/cid/like/1 //为cid为1的评论点赞
  • DELETE:删除资源 DELETE /comments/cid/1 //删除cid为1的评论

3. Response

  • 采用JSON,不要使用XML
  • 默认情况下JSON外层不需要嵌套大括号,API需要支持JSONP跨域访问或者客户端无法访问HTTP Header才需要加上嵌套大括号
  • 默认情况下不要过滤API输出中的空格,并且要支持gzip

4. API版本控制

  • 在URI中存放:GET /v1/comments;
  • 客户端在Accept Header中存放:Accept: application/vnd.github.v3+json,服务器自定义Header返回当前版本信息:X-GitHub-Media-Type: github.v3; format=json(GitHub在用);
  • 以上两种方法根据情况选择,Github用的方式是REST中所要求的方式;
  • 测试API和正式API要进行区分,方式通过如上两种方式实现。

5. 速度限制

为了避免请求泛滥,给API设置速度限制很重要。为此 RFC 6585 引入了HTTP状态码429(too many requests)。加入速度设置之后,应该提示用户,至于如何提示标准上没有说明,不过流行的方法是使用HTTP的返回头。 下面是几个必须的返回头(依照twitter的命名规则):

  • X-Rate-Limit-Limit :当前时间段允许的并发请求数
  • X-Rate-Limit-Remaining:当前时间段保留的请求数。
  • X-Rate-Limit-Reset:当前时间段剩余秒数

为什么使用当前时间段剩余秒数而不是时间戳?

时间戳保存的信息很多,但是也包含了很多不必要的信息,用户只需要知道还剩几秒就可以再发请求了这样也避免了clock skew问题。

6.缓存

HTTP提供了自带的缓存框架。你需要做的是在返回的时候加入一些返回头信息,在接受输入的时候加入输入验证。基本两种方法:

  • ETag:当生成请求的时候,在HTTP头里面加入ETag,其中包含请求的校验和和哈希值,这个值和在输入变化的时候也应该变化。如果输入的HTTP请求包含IF-NONE-MATCH头以及一个ETag值,那么API应该返回304 not modified状态码,而不是常规的输出结果。
  • Last-Modified:和etag一样,只是多了一个时间戳。返回头里的Last-Modified:包含了 RFC 1123 时间戳,它和IF-MODIFIED-SINCE一致。HTTP规范里面有三种date格式,服务器应该都能处理。

7.覆盖HTTP方法

一些HTTP客户端只支持GET和POST请求。为了能够加强这些客户端的访问能力,API需要能够覆盖HTTP方法。尽管这里没有任何强制的标准,但流行的做法是API会接收一个请求头X-HTTP-Method-Override,它的值可以是PUT、PATCH或者DELETE三者之一。

注意,用来覆盖HTTP方法的header只能在POST请求中被接受。GET请求永远不能修改服务器上的数据。

8.过滤信息

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数:

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

就像HTML的出错页面向访问者展示了有用的错误消息一样,API也应该用之前熟悉易读的格式来提供有用的错误消息。错误的表现形式应该跟其他资源保持一致,只是用一些自己的字段。

API应该一直返回合理的HTTP状态码。API错误一般情况下分成两类:代表客户端错误的400系列状态码和代表服务端错误的500系列状态码。API至少把所有400系列错误统一用易读的JSON格式来展示。如果可能(比如,如果负载均衡和反向代理能够创建自定义错误内容的话),500系列的状态码也这么弄。

JSON错误内容应该为开发者提供一些东西 – 有用的错误消息,唯一的错误码(通过它可以在文档中找到更多错误细节),可能的话提供错误细节描述。用JSON格式来输出错误看起来这样:

{  "code" : 1234,  "message" : "Something bad happened : (",  "description" : "More details about the error here"}

对于PUT、PATCH和POST的请求进行的校验错误需要嵌套多个字段。最佳做法是用固定的错误码来表示校验失败,然后在额外的errors字段中提供错误的细节,像这样:

{  "code" : 1024,  "message" : "Validation Failed",  "errors" : [
    {      "code" : 5432,      "field" : "first_name",      "message" : "First name cannot have fancy characters"
    },
    {       "code" : 5622,       "field" : "password",       "message" : "Password cannot be blank"
    }
  ]
}

10.HTTP状态码

HTTP定义了很多有意义的状态码,你可以在你的API中使用。这些状态码可以帮助API消费者用来路由它们获取到的响应内容。整理了一个你肯定会用到的状态码列表:

  • 200 OK – 对成功的GET、PUT、PATCH或DELETE操作进行响应。也可以被用在不创建新资源的POST操作上
  • 201 Created – 对创建新资源的POST操作进行响应。应该带着指向新资源地址的Location header)
  • 204 No Content – 对不会返回响应体的成功请求进行响应(比如DELETE请求)
  • 304 Not Modified – HTTP缓存header生效的时候用
  • 400 Bad Request – 请求异常,比如请求中的body无法解析
  • 401 Unauthorized – 没有进行认证或者认证非法。当API通过浏览器访问的时候,可以用来弹出一个认证对话框
  • 403 Forbidden – 当认证成功,但是认证过的用户没有访问资源的权限
  • 404 Not Found – 当一个不存在的资源被请求
  • 405 Method Not Allowed – 所请求的HTTP方法不允许当前认证用户访问
  • 410 Gone – 表示当前请求的资源不再可用。当调用老版本API的时候很有用
  • 415 Unsupported Media Type – 如果请求中的内容类型是错误的
  • 422 Unprocessable Entity – 用来表示校验错误
  • 429 Too Many Requests – 由于请求频次达到上限而被拒绝访问

11.认证

RESTful API应该是无状态。这意味着对请求的认证不应该基于cookie或者session。相反,每个请求应该带有一些认证凭证。

如果一直使用SSL,认证凭证可以简单的使用随机生成的access token,把其做为HTTP Basic Auth中user name字段的值传给API。这么做的好处是可以通过浏览器访问 – 如果浏览器从服务器收到401 Unauthorized状态码,它将会弹出一个对话框让人输出认证凭证。

当然,这种基于token来进行基本认证的方法只能当用户从API管理后台拷贝了一个token到自己的代码中才行。如果搞不到token,只能使用OAuth 2来把安全token传递给第三方。OAuth 2使用Bearer token,并且也是基于SSL来保证传输安全。

支持JSONP的API可能需要第三种方法来实现认证,因为JSONP的请求没法发送HTTP Basic Auth凭证或者Bearer token。这种情况下,可以使用一个额外的查询参数access_token。注意:使用查询参数来传递token存在一个固有的安全隐患,因为大多数web服务器会在服务器日志中保存查询参数。

不管怎么样,以上三种方法是用来在API之间传输token的方法。实际传输的token可以是一样的。

12.使用SSL

一定要使用SSL。没有例外。如今,你的web API可以从任何有互联网的地方(像图书馆,咖啡馆,机场等等)被访问到。这些地方并不都是安全的。很多地方根本没有对网络连接进行加密,如果认证凭证被劫持的话,这样访问者很容易被窃听或者被冒充。

一直使用SSL的另一个优势是,加密的连接简化了用户认证的工作 – 你可以使用简单的access token,而不需要对每个API请求进行签名。

需要注意的一件事是以非SSL的形式访问API的URL。不要把请求跳转到它们的SSL版本上。直接抛出一个严重错误!

13.Hypermedia API

RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。 比如,当用户向http://api.example.com的根目录发出请求,会得到这样一个文档。

{"link": {  "rel":   "collection https://www.example.com/comments",  "href":  "https://api.example.com/comments",  "title": "List of comments",  "type":  "application/vnd.yourformat+json"}}

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

在进行分页查询时可以返回下一页的URI,如果没有说明服务器已经取到最后一条数据了,客户端可以减少不必要的请求以及URI的构造,建议在分页的情况下使用。

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2017-03-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于 2018-09-14

API Git HTTP Apache 架构设计

https://cloud.tencent.com/developer/article/1338736

猜你喜欢

转载自blog.csdn.net/xuheng8600/article/details/84871890