Tag Archives: http

HTTP 的前世今生

作为互联网通信协议的一员老将,HTTP 协议走到今天已经经历了三次版本的变动,现在最新的版本是 HTTP2.0,相信大家早已耳熟能详。今天就给大家好好介绍一下 HTTP 的前世今生。

HTTP/0.9

HTTP 的最早版本诞生在 1991 年,这个最早版本和现在比起来极其简单,没有 HTTP 头,没有状态码,甚至版本号也没有,后来它的版本号才被定为 0.9 来和其他版本的 HTTP 区分。HTTP/0.9 只支持一种方法—— Get,请求只有一行。

  1. GET /hello.html

响应也是非常简单的,只包含 html 文档本身。

  1. <HTML>
  2. Hello world
  3. </HTML>

当 TCP 建立连接之后,服务器向客户端返回 HTML 格式的字符串。发送完毕后,就关闭 TCP 连接。由于没有状态码和错误代码,如果服务器处理的时候发生错误,只会传回一个特殊的包含问题描述信息的 HTML 文件。这就是最早的 HTTP/0.9 版本。

HTTP/1.0

1996 年,HTTP/1.0 版本发布,大大丰富了 HTTP 的传输内容,除了文字,还可以发送图片、视频等,这为互联网的发展奠定了基础。相比 HTTP/0.9,HTTP/1.0 主要有如下特性:

  •  请求与响应支持 HTTP 头,增加了状态码,响应对象的一开始是一个响应状态行
  •  协议版本信息需要随着请求一起发送,支持 HEAD,POST 方法
  •  支持传输 HTML 文件以外其他类型的内容

一个典型的 HTTP/1.0 的请求像这样:

  1. GET /hello.html HTTP/1.0
  2. User-Agent:NCSA_Mosaic/2.0(Windows3.1)
  3. 200 OK
  4. Date: Tue, 15 Nov 1996 08:12:31 GMT
  5. Server: CERN/3.0 libwww/2.17
  6. Content-Type: text/html
  7. <HTML>
  8. 一个包含图片的页面
  9. <IMGSRCIMGSRC=“/smile.gif”>
  10. </HTML>

HTTP/1.1

在 HTTP/1.0 发布几个月后,HTTP/1.1 就发布了。HTTP/1.1 更多的是作为对 HTTP/1.0 的完善,在 HTTP1.1 中,主要具有如下改进:

  •  可以复用连接
  •  增加 pipeline:HTTP 管线化是将多个 HTTP 请求整批提交的技术,而在传送过程中不需先等待服务端的回应。管线化机制须通过永久连接(persistent connection)完成。浏览器将HTTP请求大批提交可大幅缩短页面的加载时间,特别是在传输延迟(lag/latency)较高的情况下。有一点需要注意的是,只有幂等的请求可以使用 pipeline,如 GET,HEAD 方法。
  •  chunked 编码传输:该编码将实体分块传送并逐块标明长度,直到长度为 0 块表示传输结束, 这在实体长度未知时特别有用(比如由数据库动态产生的数据)
  •  引入更多缓存控制机制:如 etag,cache-control
  •  引入内容协商机制,包括语言,编码,类型等,并允许客户端和服务器之间约定以最合适的内容进行交换
  •  请求消息和响应消息都支持 Host 头域:在 HTTP1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个 IP 地址。因此,Host 头的引入就很有必要了。
  •   新增了 OPTIONS,PUT, DELETE, TRACE, CONNECT 方法

虽然 HTTP/1.1 已经优化了很多点,作为一个目前使用最广泛的协议版本,已经能够满足很多网络需求,但是随着网页变得越来越复杂,甚至演变成为独立的应用,HTTP/1.1 逐渐暴露出了一些问题:

  •  在传输数据时,每次都要重新建立连接,对移动端特别不友好
  •  传输内容是明文,不够安全
  •  header 内容过大,每次请求 header 变化不大,造成浪费
  •  keep-alive 给服务端带来性能压力

为了解决这些问题,HTTPS 和 SPDY 应运而生。

HTTPS

HTTPS 是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。

HTTPS 协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。

HTTPS 和 HTTP 的区别主要如下:

  •  HTTPS 协议使用 ca 申请证书,由于免费证书较少,需要一定费用。
  •  HTTP 是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
  • HTTP 和 HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。

SPDY

其实 SPDY 并不是新的一种协议,而是在 HTTP 之前做了一层会话层。

在 2010 年到 2015 年,谷歌通过实践一个实验性的 SPDY 协议,证明了一个在客户端和服务器端交换数据的另类方式。其收集了浏览器和服务器端的开发者的焦点问题,明确了响应数量的增加和解决复杂的数据传输。在启动 SPDY 这个项目时预设的目标是:

  •  页面加载时间 (PLT) 减少 50%。
  •  无需网站作者修改任何内容。
  •  将部署复杂性降至最低,无需变更网络基础设施。
  •  与开源社区合作开发这个新协议。
  •  收集真实性能数据,验证这个实验性协议是否有效。

为了达到降低目标,减少页面加载时间的目标,SPDY 引入了一个新的二进制分帧数据层,以实现多向请求和响应、优先次序、最小化及消除不必要的网络延迟,目的是更有效地利用底层 TCP 连接。

HTTP/2.0

时间来到 2015 年,HTTP/2.0 问世。先来介绍一下 HTTP/2.0 的特点吧:

  •  使用二进制分帧层:在应用层与传输层之间增加一个二进制分帧层,以此达到在不改动 HTTP 的语义,HTTP 方法、状态码、URI 及首部字段的情况下,突破HTTP1.1 的性能限制,改进传输性能,实现低延迟和高吞吐量。在二进制分帧层上,HTTP2.0 会将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码,其中 HTTP1.x 的首部信息会被封装到 Headers 帧,而我们的 request body 则封装到 Data 帧里面。

  •  多路复用:对于 HTTP/1.x,即使开启了长连接,请求的发送也是串行发送的,在带宽足够的情况下,对带宽的利用率不够,HTTP/2.0 采用了多路复用的方式,可以并行发送多个请求,提高对带宽的利用率。

  •  数据流优先级:由于请求可以并发发送了,那么如果出现了浏览器在等待关键的 CSS 或者 JS 文件完成对页面的渲染时,服务器却在专注的发送图片资源的情况怎么办呢?HTTP/2.0 对数据流可以设置优先值,这个优先值决定了客户端和服务端处理不同的流采用不同的优先级策略。
  •  服务端推送:在 HTTP/2.0 中,服务器可以向客户发送请求之外的内容,比如正在请求一个页面时,服务器会把页面相关的 logo,CSS 等文件直接推送到客户端,而不会等到请求来的时候再发送,因为服务器认为客户端会用到这些东西。这相当于在一个 HTML 文档内集合了所有的资源。
  •  头部压缩:使用首部表来跟踪和存储之前发送的键值对,对于相同的内容,不会再每次请求和响应时发送。

可以看到 HTTP/2.0 的新特点和 SPDY 很相似,其实 HTTP/2.0 本来就是基于 SPDY 设计的,可以说是 SPDY 的升级版。

但是 HTTP/2.0 仍有和 SPDY 不同的地方,主要有如下两点:

  •  HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS。
  •  HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE。

from:http://developer.51cto.com/art/201811/586932.htm

HTTP协议

我们知道目前很多应用系统中的内容传输协议采用的HTTP协议,因此不管你是前端人员、后端人员、运维人员,甚至是管理人员,都需要掌握HTTP知识!!

HTTP发展历史

HTTP/0.9  

该版本只有一个命令GET;没有HEADER等描述数据的信息; 服务器发送完毕,就关闭TCP连接。

HTTP/1.0  

该版本增加了很多命令;增加status code 和header;多字符集支持、多部分发送、权限、缓存等。

HTTP/1.1   

该版本增加了持久连接Pipeline,增加host和其他一些命令。持久连接会在HTTP特性中介绍;如果没有pipeline,那么Web服务器就需要串行处理请求,而有了pipeline,Web服务器就并行处理请求;而增加host实现了一台物理设备可以运行多个web服务。

HTTP/2.0   

所有数据以二进制传输,之前版本使用字符串进行传输;同一个连接 里面发送多个请求不再需要按照顺序来;头信息压缩以及推送等提高效率的功能。

HTTP三次握手

为什么要三次握手?因为网络是有可能延迟的,当客户端没有收到服务端的确认包,如果没有第三次握手,那么服务端不知道上次传输是不是被客户端正常接收了,如果没有接收,服务端的这个端口也是打开的,这就比较浪费资源。

HTTP报文

HTTP报文分为请求报文响应报文,请求报文和响应报文分为起始行、首部(header)和主体(body),请求报文的首部包括三部分,分别是HTTP方法、资源目录和协议,而响应报文的首部包括协议版本、状态码和状态吗对应的意思,比如200状态的意思是ok。需要注意的是:HTTP header和HTTP body之间以一行分隔。

HTTP方法  

HTTP方法定义对资源的操作,常用的有GET、POST等,这就就不详细展开了。

HTTP Code  

HTTP Code用于定义服务器对请求的处理结果,各个区间的code有不用的语义。1xx  表示信息响应类,表示接收到请求并且继续处理;2xx 表示成功;3xx 表示重定向;4xx 表示客户端出错;5xx 表示服务器出错。

HTTP特性

跨域请求   

同源策略,也就是说当两个请求的URL的协议、host和端口都相同的情况下,我们才认为这两个请求是同域的即同源,而只要协议、host和端口只要有一项是不同的,我们就认为是不同源的,即跨域,例如:

http://www.mukedada.com:80

http://www.mukedada.com:8080

上述两个请求就是跨域请求。需要注意的是跨域请求不是说浏览器限制了发起跨站请求,浏览器只是将返回结果拦截下来,最好的例子就是CSRF跨站脚本攻击。如果我们想让浏览器放行返回结果,则通过以下方法:

  1. 服务端设置Access-Control-Allow-Origin参数为允许,例如’Access-Control-Allow-Origin’ : ‘*’
  2. <link>、<img>和<script>三标签中的请求是允许跨域的,这也是JSONP的跨域做法。
Cache  Control 

对于静态资源,比如说image、js等,它们是不会经常方式变更的,而且它们的容量比较大,如果我们每次访问都要从服务器从获取相应数据,那么性能就会变得比较差,因此HTTP协议定义一些和缓存相关的参数。

可缓存性,表示在哪些地方可以缓存,比如说客户端浏览器、代理服务器等,它有三个常用的参数:public、private、no-cache。public 表明响应可以被任何对象缓存,包括发送请求的客户端浏览器、代理服务器等等;private 表示响应只能被单个用户缓存,不能作为共享缓存,即代理服务器不能缓存它;no-cache表明强制所有缓存了该响应的缓存用户,在使用已存储的缓存之前,发送带验证器的请求到源始服务器。

到期,max-age=<seconds>,设置缓存存储的最大周期,超过这个时间缓存就被认为过期。s-maxage=<seconds> 它的作用域仅在共享缓存(比如各个代理)。max-stale=<seconds> 表明客户端愿意接收一个已过期的资源。

验证,must-revalidate,缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。proxy-revalidate,与must-revalidate作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略。

其他。no-store,客户端和代理服务器不存储任何缓存,而是直接从服务器获取内容。no-transform:不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理可以对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。 no-transform指令不允许这样做。

Cookie   

服务端通过Set-Cookie将相关数据保存到浏览器中,而这些相关数据就是Cookie,那么,下次在同域的请求中就会带上这些Cookie,Cookie是键值对,可以设置多个。Cookie中通过max-age和expires设置过期时间,Secure值在https的时候发送,HttpOnly无法通过document.cookie访问。具体可以参考Session 和 Cookie

资源验证  

在Cache Control中我们介绍当设置no-cache参数时,表明每次请求都要到服务器验证,验证结果表明可以读取本地缓存才可以从本地读取缓存。只有到数据发生修改时,我们才需要从服务端读取最新数据,否则从本地读取缓存。此时,判断数据是否发生修改就变得尤为重要,通常我们采用Last-Modfied和Etag两个验证头来验证数据是否发生修改。其中Last-Modifed 通常配合If-Modified-Since或者If-UnModified-Since使用,而Etag 通常配合If-Match或者If-Non-Match使用。为了帮助大家理解,我举一个栗子。假设我们访问mudedada.com返回头信息包含:

Last-Modified:888

Etag:123

下一次访问mukedada.com的请求头中就会包含:

If-Modified-Since:888

If-Non-Match:123

服务器会比较请求头中的Last-Modified、Etag 和服务器中的对应值是否相同,如果不相同则重新获取,否则从本地缓存中获取。

长连接   

我们知道一个HTTP需要创建一个TCP连接,完成之后就关闭TCP连接,这个成本比较高(因为创建一个TCP连接需要通过三次握手),所以在HTTP/1.1开始支持长连接,请求头标识是Connection:keep-alive。如下图所示,同一个Connection ID表示同一个连接。需要注意的是同一个连接只能是同域请求。

数据协商   

数据协商指的是客户端向服务端发送请求时,客户端会声明它希望服务端返回个格式是什么?服务端根据客户端的声明来判断返回什么要的数据。其中客户端通过Accept、Accept-Encoding等参数进行设置,而服务端通过Content-Type等参数进行设置。

客户端相关参数

  1. Accept指定返回数据类型;
  2. Accept-Encoding指定服务端的数据压缩方式,目前服务端的压缩算法有gzip, deflate, br等;
  3. Accept-Language指定返回数据的语言,例如 Accept-Language:  zh-CN,zh;q=0.9,en;q=0.8,其中q表示的是权重,也就是说浏览器更希望服务器返回的是中文;
  4. User-Agent表示浏览器的相关信息,它能区分是移动端浏览器还是PC端浏览器,从而返回特定的页面。

服务端相关参数:

  1. Content-Type指的是服务端返回的数据类型;
  2. Content-Encoding对应客户端的Accept-Encoding,指的是数据压缩方式;
  3. Content-Language服务端语言。

from:https://mp.weixin.qq.com/s/vRQ2zuKxyLaBxcm9lolL7w

HTTP协议冷知识大全

如果不用HTTPS,HTTP协议如何安全的传输密码信息?

HTTP协议是纯文本协议,没有任何加密措施。通过HTTP协议传输的数据都可以在网络上被完全监听。如果用户登陆时将用户名和密码直接明文通过HTTP协议传输过去了,那么密码可能会被黑客窃取。
一种方法是使用非对称加密。GET登陆页面时,将公钥以Javascript变量的形式暴露给浏览器。然后用公钥对用户的密码加密后,再将密码密文、用户名和公钥一起发送给服务器。服务器会提前存储公钥和私钥的映射信息,通过客户端发过来的公钥就可以查出对应的私钥,然后对密码密文进行解密就可以还原出密码的明文。
为了加强公钥私钥的安全性,服务器应该动态生成公钥私钥对,并且使用后立即销毁。但是动态生成又是非常耗费计算资源的,所以一般服务器会选择Pool方法提供有限数量的公钥私钥对池,然后每隔一段时间刷新一次Pool。

640文件路径攻击

很多操作系统都会使用..符号表示上层目录。如果黑客在URL的路径里面使用..符号引用上层目录,而服务器没有做好防范的话就有可能导致黑客可以直接访问权限之外的文件。比如使用多级..符号就可以引用到根目录,进一步就可以访问任意文件。
所以很多服务器都禁止在URL路径里出现..符号以避免被攻击。
文件路径攻击也是很多黑客非常喜爱使用的攻击方法之一。如果你的服务器有一定的访问量,打开你的nginx日志,你就会偶尔发现有一些奇怪的URL里面有一堆..符号,这种URL的出现就表示网络上的黑客正在尝试攻击你的服务器。

DNS欺骗

HTTP协议严重依赖于DNS域名解析。任意一个域名类网址的访问都需要经过域名解析的过程得到目标服务的IP地址才能成功继续下去。
如果掌管DNS服务的运营商作恶将域名解析到不正确的IP,指向一个钓鱼的网页服务。用户如果没有觉察,就可能会将自己的敏感信息提交给冒牌的服务器。

642谨慎使用外部的HTTP代理

HTTP代理作为客户端到服务器之间的中间路由节点,它起到传话人和翻译官的角色。
如果这个翻译官不靠谱的话,客户端是会拿到错误的返回数据的。它同DNS欺骗一样,是可以对客户端进行钓鱼攻击的。
如果这个翻译官口风不严的话,它可能会将它听到的敏感信息泄露给别人

643413 Request Entity Too Large

客户端上传图片太大超过服务器限制时,服务器返回413错误。

414 Request-URI Too Long

客户端访问的URI太长,超出了服务器允许限制,服务器返回414错误。

202 Accepted

常用于异步请求。客户端发送请求到服务器,服务器立即返回一个202 Accepted表示已经成功接收到客户端的请求。
后面怎么处理由服务器自己决定,一般服务器会给客户端预留一个可以查询处理状态的接口,客户端可以选择轮训该接口来知道请求的处理进度和结果。

POST提交数据的方式

application/x-www-form-urlencoded

提交数据表单时经常使用,Body内部存放的是转码后的键值对。

POST http://xyz.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
a=1&b=2&c=3&c=4

application/json

提交结构化表单时使用,Body内部存放的是JSON字符串。ElasticSearch的查询协议使用的是这种方式。

POST http://xyz.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"a": 1, "b": 2, "c": [3, 4]}

multipart/form-data

上传文件时经常使用。这种格式比较复杂,它是为了支持多文件上传混合表单数据而设计的一种特殊的格式。

<form action="http://example.com/upload" method="post" enctype="multipart/form-data">
  <p><input type="text" name="key1" value="value1">
  <p><input type="text" name="key2" value="value2">
  <p><input type="file" name="file1">
  <p><input type="file" name="file2">
  <p><button type="submit">Submit</button>
</form>

用户填充了表单设置了待上传的文件,点击Submit,传输数据大致如下

POST /upload HTTP/1.1
Content-Length:xxxxx
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryKOThiwE6HubGib7j
Host:example.com
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="key1"
value1
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="key2"
value2
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="file1"; filename="file1name.png"
Content-Type: image/png
file1 content here
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="file2"; filename="file2name.jpeg"
Content-Type: image/jpeg
file2 content here
------WebKitFormBoundaryKOThiwE6HubGib7j--

Cookie

浏览器请求的Cookie中往往会携带敏感信息。服务器一般会将当前用户的会话ID存在cookie里,会话的具体内容存在服务器端,会话的内容很敏感。

浏览器请求时会携带Cookie信息,服务器根据Cookie信息中的会话ID找到对应的会话内容。会话内容里可能存储了用户的权限信息,拿到这部分权限信息后就可能随意控制修改用户的数据。

644因为HTTP协议的不安全性,请求数据包很容易被窃听,Cookie中的会话信息很容易被盗。解决方案之一就是在会话中记录用户的终端信息和IP地址信息,如果这些信息突然发生改变,需要强制用户重新认证。

不过高级的黑客是可以伪造出和用户真实请求一摸一样的数据包的。最彻底的解决方案还是采用HTTPS协议。

普通的Cookie信息可以通过Javascript脚本获取到。如果黑客通过某种方式在网页中植入不安全的脚本,将用户的Cookie拿到然后发送到远程的第三方服务器中,那么Cookie中的信息就被泄露了。

Cookie的两个重要属性

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

被标记为Secure的Cookie信息在HTTP请求中不会被传送,它只会在HTTPS请求中传送,避免数据被泄露。

被标记为HttpOnly的Cookie信息是无法通过Javascript API获取到的,它只会在请求中传送。这样可以避免黑客通过网页脚本方式窃取Cookie中的敏感信息。

Cookie(甜点)如此好吃,黑客们总想通过Cookie做各种文章。

645CSRF(Cross-Site Request Forgery)

CSRF跨站请求伪造有很多别名,比如One-Click Attack(一键攻击),比如Session Riding(搭便车攻击)

假设在在一个社区博客网站中,删除个人的文章只需要一个URL就可以,Cookie中的会话权限信息会自动附加到请求上。

# 123456为文章的ID
http://example.com/blog/123456/delete

那么当别人伪造了一个上面的链接地址诱惑你去点击,比如通过站内信件、私聊、博客评论、图片链接或者在别的什么网站上随机制造的一个链接。你不经意点了一下,就丢了你的文章。所以它被称为一键攻击。因为这是借用了你当前登陆的会话信息来搞事,所以也被称为搭便车攻击。

如果在一个金融系统中,转账要是也可以通过一个简单的URL进行的话,那这种危险就非同小可。

646这就要求修改性的操作务必不得使用简单的GET请求进行处理。但是即使这种情况下你改成了POST请求,黑客依然有办法伪造请求,那就是通过iframe。

黑客在别的什么网站上伪造了一个POST表单,诱惑你去submit。如果只是普通的内嵌进HTML网页的表单,用户提交时会出现跨域问题。因为当前网站的域名和表单提交的目标域名不一致。但是如果通过iframe来内嵌表单,则可以绕过跨域的问题,而用户却完全没有任何觉察。

为了防范CSRF攻击,聪明的网站的POST表单里都会带上CSRF_TOKEN这个隐藏字段。CSRF_TOKEN是根据用户的会话信息生成的。当表单提交时,会将token和用户的会话信息做比对。如果匹配就是有效的提交请求。

<form method="POST" action="/blog/delete">
<label for="blog_id">博客ID</label>
<input type="text" name="blog_id" value="12345">
<input type="hidden" name="csrf_token" value="xxxxxxxxxxxx">
</form>

黑客必须拿到CSRF_TOKEN才可以借用用户的会话信息实施CSRF攻击,但是CSRF_TOKEN又必须由用户的会话信息才可以生成。黑客没有用户的会话信息,从而无法实施CSRF攻击。

XSS(Cross Site Scripting)

如果黑客可以在你的网页中植入任意Javascript脚本,那他就可以随意鱼肉你的账户。通过Javascript可以获取Cookie的信息,可以借用你的会话去调用一些隐秘的API,而这一些行为都是在偷偷的进行,你根本完全不知道。

<div>
# 用户内容Start
<script>send_to_hacker(document.cookie)</script>
# 用户内容END
</div>

这类攻击在一些UGC网站中非常常见,常见的博客类网站就是UGC网站,用户可以通过编辑内容来生成网页。

黑客也是用户。他可以编辑一段Javascript脚本作为内容提交上去。如果服务器没有做好防范,这段脚本就会在生成的网页中运行起来。当其它用户在登陆的状态下来浏览这个网页的时候,就悲剧了。

防范XSS一般是通过对输出的内容进行内容替换做到的。在HTML页面中不同的位置会有不同的内容替换规则。
比较常见的是使用HTML entity编码将HTML标签之间的内容中的一些特殊的字符进行转码。

<div>
# safe now
&lt;script&gt;send_to_hacker(document.cookie)&lt;/script&gt;
</div>

还有些UGC内容在HTML标签的属性中、Javascript的变量中、URL、css代码中,他们转码的规则并不一样,具体方法可以去Google相关文档。

跨域

跨域是个很头痛的问题。

当你有多个后端服务,但是只有一个前端的时候,你想做前后端分离,就会遇到跨域问题。你发现你的前端js调用后端服务时控制台告诉你不ok。然后只好把这些服务都挂在了同一个nginx域名下面,通过url前缀区分。

647这时候你会想,跨域太TM讨厌了。既然跨域这么讨厌,那为什么浏览器非要限制跨域呢?

还是安全原因。

让我们回到上文的搭便车攻击(Session Riding),也就是骑着别人的会话来搞事情。

假设现在你的浏览器开了一个站点A,登陆了进去,于是cookie便记录了会话id。
然后你又不小心开了另一个站点B,这个站点页面一打开就开始执行一些恶意代码。这些代码的逻辑是调用站点A的API来获取站点A的数据,因为可以骑着(Ride)站点A的会话cookie。而这些数据正好是用户私密性的。于是用户在站点A上的私有信息就被站点B上的代码窃走了。这就是跨域的风险。

但是有时候我们又希望共享数据给不同的站点,该怎么办呢?

答案是JSONP & CORS

JSONP(JSON Padding)

JSONP通过HTML的script标记实现了跨域共享数据的方式。JSON通过在网页里定义一个回调方法,然后在页面上插入一个动态script标签,指向目标调用地址。服务器会返回一段javascript代码,一般是some_callback(data)这种形式的回调。该段代码会在浏览器里自动执行,于是网页就得到了跨域服务器返回的数据。

<script>
function some_callback(data) {
    console.log(data)
}
</script>
<script src="http://example.com/someapi?callback=some_callback"></script>

因为JSONP是不携带cookie信息的,所以能有效避免搭便车攻击。JSONP是否可以获取到数据还需要服务器对这种调用提供显示支持,服务器必须将数据以javascript代码的形式返回才可以传递给浏览器。

CORS(Cross-Origin Resource Sharing)

JSONP的不足在于它只能发送GET请求,并且不能携带cookie。而CORS则可以发送任意类型的请求,可以选择性携带cookie。

CORS是通过Ajax发送的跨域请求技术。CORS的请求分为两种,一种是简单请求,一种是复杂请求。简单请求就是头部很少很简单的GET/HEAD/POST请求。复杂请求就是非简单请求。

浏览器发现Ajax的请求是跨域的,就会在请求头添加一个Origin参数,指明当前请求的发起站点来源。服务器根据Origin参数来决定是否授权。

如果是简单请求,Ajax直接请求服务器。服务器会当成普通的请求直接返回内容,不同的是还会在响应头部添加几个重要的头部,其中最重要的头部是Access-Control-Allow-Origin: http://example.com

浏览器如果在响应中没有读到这个头部,就会通知Ajax请求失败。虽然服务器返回了数据,浏览器也不让脚本读到数据,这就保证了跨域的安全。服务器就是通过请求的Origin参数来决定要不要响应Access-Control-Allow-Origin头部来决定是否允许指定网站的跨域请求。

如果是复杂请求,要走一个预检的流程。预检就是浏览器先向服务器发送一个Method为Options的请求,如果服务器允许跨域请求,浏览器再发起这个Ajax请求。所以CORS的复杂请求会比简单请求额外耗费一个TTL的时间。

CORS的细节请参见大神阮一峰的博文《跨域资源共享CORS详解》

from:https://mp.weixin.qq.com/s/aekcsgLG6jZw3LeF3R9ssQ

一次完整的HTTP请求与响应涉及了哪些知识

本文以HTTP请求和响应的过程来讲解涉及到的相关知识点。

一、 HTTP请求和响应步骤

一次完整的HTTP请求与响应涉及了哪些知识?

图片来自: 理解Http请求与响应

以上完整表示了HTTP请求和响应的7个步骤,下面从TCP/IP协议模型的角度来理解HTTP请求和响应如何传递的。

二、TCP/IP协议

TCP/IP协议模型(Transmission Control Protocol/Internet Protocol),包含了一系列构成互联网基础的网络协议,是Internet的核心协议,通过20多年的发展已日渐成熟,并被广泛应用于局域网和广域网中,目前已成为事实上的国际标准。TCP/IP协议簇是一组不同层次上的多个协议的组合,通常被认为是一个四层协议系统,与OSI的七层模型相对应。

HTTP协议就是基于TCP/IP协议模型来传输信息的。

一次完整的HTTP请求与响应涉及了哪些知识?

(1). 链路层

也称作数据链路层或网络接口层(在第一个图中为网络接口层和硬件层),通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。ARP(地址解析协议)和RARP(逆地址解析协议)是某些网络接口(如以太网和令牌环网)使用的特殊协议,用来转换IP层和网络接口层使用的地址。

(2). 网络层

也称作互联网层(在第一个图中为网际层),处理分组在网络中的活动,例如分组的选路。在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议)。

IP是一种网络层协议,提供的是一种不可靠的服务,它只是尽可能快地把分组从源结点送到目的结点,但是并不提供任何可靠性保证。同时被TCP和UDP使用。TCP和UDP的每组数据都通过端系统和每个中间路由器中的IP层在互联网中进行传输。

ICMP是IP协议的附属协议。IP层用它来与其他主机或路由器交换错误报文和其他重要信息。

IGMP是Internet组管理协议。它用来把一个UDP数据报多播到多个主机。

(3). 传输层

主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议族中,有两个互不相同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议)。

TCP为两台主机提供高可靠性的数据通信。它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,设置发送最后确认分组的超时时钟等。由于运输层提供了高可靠性的端到端的通信,因此应用层可以忽略所有这些细节。为了提供可靠的服务,TCP采用了超时重传、发送和接收端到端的确认分组等机制。

UDP则为应用层提供一种非常简单的服务。它只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端。一个数据报是指从发送方传输到接收方的一个信息单元(例如,发送方指定的一定字节数的信息)。UDP协议任何必需的可靠性必须由应用层来提供。
(4). 应用层

应用层决定了向用户提供应用服务时通信的活动。TCP/IP 协议族内预存了各类通用的应用服务。包括 HTTP,FTP(File Transfer Protocol,文件传输协议),DNS(Domain Name System,域名系统)服务。

一次完整的HTTP请求与响应涉及了哪些知识?

当应用程序用TCP传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络。其中每一层对收到的数据都要增加一些首部信息(有时还要增加尾部信息),该过程如图所示。

一次完整的HTTP请求与响应涉及了哪些知识?

当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用(Demultiplexing)。协议是通过目的端口号、源I P地址和源端口号进行解包的。

通过以上步骤我们从TCP/IP模型的角度来理解了一次HTTP请求与响应的过程。

下面这张图更清楚明白:

一次完整的HTTP请求与响应涉及了哪些知识?

下面具体来看如何进行一步步操作的。

三、TCP三次握手

TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。

一次完整的HTTP请求与响应涉及了哪些知识?

第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;

第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

为什么要三次握手

为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

具体例子:“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

四、HTTP协议

Http是什么?

通俗来讲,他就是计算机通过网络进行通信的规则,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据。目前任何终端(手机,笔记本电脑。。)之间进行任何一种通信都必须按照Http协议进行,否则无法连接。

四个基于:

请求与响应:客户端发送请求,服务器端响应数据

无状态的:协议对于事务处理没有记忆能力,客户端第一次与服务器建立连接发送请求时需要进行一系列的安全认证匹配等,因此增加页面等待时间,当客户端向服务器端发送请求,服务器端响应完毕后,两者断开连接,也不保存连接状态,一刀两断!恩断义绝!从此路人!下一次客户端向同样的服务器发送请求时,由于他们之前已经遗忘了彼此,所以需要重新建立连接。

应用层:Http是属于应用层的协议,配合TCP/IP使用。

TCP/IP:Http使用TCP作为它的支撑运输协议。HTTP客户机发起一个与服务器的TCP连接,一旦连接建立,浏览器(客户机)和服务器进程就可以通过套接字接口访问TCP。

针对无状态的一些解决策略:

有时需要对用户之前的HTTP通信状态进行保存,比如执行一次登陆操作,在30分钟内所有的请求都不需要再次登陆。于是引入了Cookie技术。

HTTP/1.1想出了持久连接(HTTP keep-alive)方法。其特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态,在请求首部字段中的Connection: keep-alive即为表明使用了持久连接。
等等还有很多。。。。。。

下面开始讲解重头戏:HTTP请求报文,响应报文,对应于上述步骤的2,3,4,5,6。

HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的。HTTP有两类报文:请求报文和响应报文。

五、HTTP请求报文

一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。

一次完整的HTTP请求与响应涉及了哪些知识?

1.请求行

请求行分为三个部分:请求方法、请求地址和协议版本

请求方法

HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE。

最常的两种GET和POST,如果是RESTful接口的话一般会用到GET、POST、DELETE、PUT。

请求地址

URL:统一资源定位符,是一种自愿位置的抽象唯一识别方法。

组成:<协议>://<主机>:<端口>/<路径>

端口和路径有时可以省略(HTTP默认端口号是80)

如下例:

一次完整的HTTP请求与响应涉及了哪些知识?

有时会带参数,GET请求

协议版本

协议版本的格式为:HTTP/主版本号.次版本号,常用的有HTTP/1.0和HTTP/1.1

2.请求头部

请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。

常见请求头如下:

一次完整的HTTP请求与响应涉及了哪些知识?

请求头部的最后会有一个空行,表示请求头部结束,接下来为请求数据,这一行非常重要,必不可少。

3.请求数据

可选部分,比如GET请求就没有请求数据。

下面是一个POST方法的请求报文:

POST  /index.php HTTP/1.1    请求行
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2  请求头
Accept: text/html,application/xhtml+xml,application/xml;q=0.9, /;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost/
Content-Length:25
Content-Type:application/x-www-form-urlencoded
空行
username=aa&password=1234  请求数据

六、HTTP响应报文

一次完整的HTTP请求与响应涉及了哪些知识?

HTTP响应报文主要由状态行、响应头部、空行以及响应数据组成。

1.状态行

由3部分组成,分别为:协议版本,状态码,状态码描述。

其中协议版本与请求报文一致,状态码描述是对状态码的简单描述,所以这里就只介绍状态码。

状态码

状态代码为3位数字。
1xx:指示信息–表示请求已接收,继续处理。
2xx:成功–表示请求已被成功接收、理解、接受。
3xx:重定向–要完成请求必须进行更进一步的操作。
4xx:客户端错误–请求有语法错误或请求无法实现。
5xx:服务器端错误–服务器未能实现合法的请求。

下面列举几个常见的:

一次完整的HTTP请求与响应涉及了哪些知识?

2.响应头部

与请求头部类似,为响应报文添加了一些附加信息

常见响应头部如下:

一次完整的HTTP请求与响应涉及了哪些知识?

3.响应数据

用于存放需要返回给客户端的数据信息。

下面是一个响应报文的实例:

HTTP/1.1 200 OK  状态行
Date: Sun, 17 Mar 2013 08:12:54 GMT  响应头部
Server: Apache/2.2.8 (Win32) PHP/5.2.5
X-Powered-By: PHP/5.2.5
Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 4393
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
空行

<html>  响应数据
<head>
<title>HTTP响应示例<title>
</head>
<body>
Hello HTTP!
</body>
</html>

关于请求头部和响应头部的知识点很多,这里只是简单介绍。

通过以上步骤,数据已经传递完毕,HTTP/1.1会维持持久连接,但持续一段时间总会有关闭连接的时候,这时候据需要断开TCP连接。

七、TCP四次挥手

当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那对于TCP的断开连接,这里就有了神秘的“四次分手”。

一次完整的HTTP请求与响应涉及了哪些知识?

第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;

第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;

第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。

为什么要四次分手

TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。

通过以上步骤便完成了HTTP的请求和响应,进行了数据传递,这其中涉及到需要知识点,都进行了逐一了解。

参考文章:

你需要了解的HTTP知识都在这里了!
HTTP知识点总结
理解Http请求与响应
HTTP-请求、响应、缓存
你应该知道的HTTP基础知识
整理Http知识点
简析TCP的三次握手与四次分手
HTTP请求报文和HTTP响应报文
TCP/IP协议簇分层详解
HTTP请求报文和HTTP响应报文

from:http://www.shellsec.com/news/37745.html

理解HTTP幂等性

基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢?我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议,无论是服务器端提供Web服务,还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展,互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA(富互联网应用)过渡的趋势。Web API专注于提供业务服务,RIA专注于用户界面和交互设计,从此两个领域的分工更加明晰。在这种趋势下,Web API设计将成为服务器端程序员的必修课。然而,正如简单的Java语言并不意味着高质量的Java程序,简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API,还需要深入理解分布式系统及HTTP协议的特性。

幂等性定义

本文所要探讨的正是HTTP协议涉及到的一种重要性质:幂等性(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方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一样,HTTP规范也没有办法通过消息格式等语法手段来定义它,这可能是它不太受到重视的原因之一。但实际上,幂等性是分布式系统设计中十分重要的概念,而HTTP的分布式本质也决定了它在HTTP中具有重要地位。

分布式事务 vs 幂等设计

为什么需要幂等性呢?我们先从一个例子说起,假设有一个从账户取钱的远程API(可以是HTTP的,也可以不是),我们暂时用类函数的方式记为:

bool withdraw(account_id, amount)

withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:

20110810171503575

图1

这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。

另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:

int create_ticket() 
bool idempotent_withdraw(ticket_id, account_id, amount)

create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。

基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。如图2所示:

201106042051069339

图2

和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。

HTTP的幂等性

HTTP协议本身是一种面向资源的应用层协议,但对HTTP协议的使用实际上存在着两种不同的方式:一种是RESTful的,它把HTTP当成应用层协议,比较忠实地遵守了HTTP协议的各种规定;另一种是SOA的,它并没有完全把HTTP当成应用层协议,而是把HTTP协议作为了传输层协议,然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协议,它是分布式系统的一种特性;所以,不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。

HTTP GET方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。

HTTP DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。

比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源,PUT表示更新资源”;而实际上,二者均可用于创建资源,更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的:
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line …… If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.
POST所对应的URI并非创建的资源本身,而是资源的接收者。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法不具备幂等性。而PUT所对应的URI是要创建或更新的资源本身。比如:PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有幂等性。

在介绍了几种操作的语义和幂等性之后,我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单,用POST /tickets来实现create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分,真正的URI应该是/accounts/account_id/ticket_id,而amount应该放在请求的body中。这种模式可以应用于很多场合,比如:论坛网站中防止意外的重复发帖。

总结

上面简单介绍了幂等性的概念,用幂等设计取代分布式事务的方法,以及HTTP主要方法的语义和幂等性特征。其实,如果要追根溯源,幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同,有兴趣的读者可以从Wikipedia上进一步了解。

参考

RFC 2616, Hypertext Transfer Protocol — HTTP/1.1, Method Definitions
The Importance of Idempotence
Stackoverflow – PUT vs POST in REST

from:http://www.cnblogs.com/weidagang2046/archive/2011/06/04/2063696.html