如果只能用一句话描述两者区别,那就是GET获取数据;POST提交数据。这是重中之重,一切的缘由。
在进入正题之前,你首先要知道知道HTTP是什么,HTTP 全称Hypertext Transfer Protocol,译为超文本传输协议,更学究点的说法是超文本转移协议,是一个web通信协议。请注意,它是一份协议,这意味着这是一套有各种不同实现的约定。他有一份比那本定价109RMB的《HTTP权威指南》还权威的文档,而且,这份文档是完全免费和公开的。绝大多数关于HTTP的问题你都可以在IETE的官方文档上得到准确答案。
我们一般使用的HTTP文档是<RFC2616 (HTTP/1.1)>
在HTTP动词方面,建议使用后来的修订版 <RFC7231(HTTP/1.1: Semantics and Content)>
协议语义/定义
协议语义是这两个HTTP方法最根本最本质的区别。
POST是什么?
<RFC7231>
The POST method requests that the target resource process the
representation enclosed in the request according to the resource's
own specific semantics. For example, POST is used for the following
functions (among others):o Providing a block of data, such as the fields entered into an HTML
form, to a data-handling process;o Posting a message to a bulletin board, newsgroup, mailing list,
blog, or similar group of articles;o Creating a new resource that has yet to be identified by the
origin server; ando Appending data to a resource's existing representation(s).
POST方法请求目标资源根据自身语义处理POST请求附带的表述。
示例上的功能2和功能3我们很少会用到,主要谈谈功能1和功能4.
功能4:将数据(作为子级)附加到资源的现有表述:
这个用法被称为POST-to-Append,主要在RESTfulAPI中使用。
功能1:提供如HTML表单字段之类的数据给(服务端)数据处理程序。这种用法就是最为我们熟悉的POST用法。实际上由于这个说法过于模糊和宽泛,模糊到POST几乎可以替代其他所有向服务器提交数据的副作用方法,而且HTML标准迟迟不支持PUT,DELETE,PATCH等方法,POST已经成为了事实上的向服务器提交数据的万金油方法了,也就是文章开头说到的"POST提交数据"。这种用法也称为Overloaded POST
GET是什么?
<RFC7231>
The GET method requests transfer of a current selected representation
for the target resource.
GET方法请求传输目前资源选定的表述。
从协议语义上,GET和POST的第一个区别已经很明显了:
GET是用来向获取服务器信息的,请求报文传输的信息只是用于描述所需资源的参数,返回的信息才是数据本身;POST是用来向服务器传递数据的,其请求报文传递的信息就是数据本身,返回的报文只是操作的结果。这是GET和POST最重要的区别,没有之一。
安全(Security)与安全(Safe)
很多人说“POST比GET安全。但其实安全(Safe)作为一个重要的HTTP术语,在HTTP协议中有特殊的语义。
<RFC7231>
Request methods are considered "safe" if their defined semantics are
essentially read-only; i.e., the client does not request, and does
not expect, any state change on the origin server as a result of
applying a safe method to a target resource. Likewise, reasonable
use of a safe method is not expected to cause any harm, loss of
property, or unusual burden on the origin server.
...
Of the request methods defined by this specification, the GET, HEAD,
OPTIONS, and TRACE methods are defined to be safe.
简单来说HTTP的安全(Safe)指的是是否改变服务器资源的状态,即是我们平常说的有无副作用。因为提交数据的目的往往是为了改变服务器状态,所以POST不是安全的(Safe);而GET是为了获取数据,所以他不应该改变服务器的状态,是安全的(Safe)。HTTP中的安全Safe(副作用)和大多数人平时想的安全Security(例如数据安全),仅仅是共用一个中文词汇,实质上就是雷峰和雷峰塔的关系,从这个角度上来说GET反而比POST安全多了。为此我建议大家把安全留给更加常用的Security,中文使用一个更加少歧义的说法来描述GET和POST的第二个区别:
POST是一个可能有副作用的方法,但GET应是没有副作用的的。
说句题外话,现代浏览器和其他客户端为了用户体验可能会根据你的行为预先提交一个GET请求,但绝对不敢预提交POST请求,你猜猜是为什么?
POST的安全性(Security)
常常看到的一个论证POST比GET安全的论点是:“使用HTTP报文体段传输的POST报文比使用URL传递的GET更加安全,因为不会被泄露在地址栏上。”
但事实上,但凡有一些WEB安全意识或者HTTP抓包经验的行业人员都会明白,能抓到或会留有URL痕迹的地方,HTTP报文的有效载荷基本也看得清清楚楚,不展示在URL只能算是聊胜于无,不经过加密的情况下使用URL和报文body传递机密数据,就好比裸体上街和穿一条丁字裤上街。如果你有任何安全上的需求,请先上HTTPS,POST不保证你的请求是安全的(Security)。
那么POST还有什么比GET安全(Security)的地方呢?还是能举例的:
- 用户可能会在分享链接时,会把你放在URL里的鉴权TOKEN一起分享给他的朋友。
- 如果你的GET请求是有副作用的,你可能会被通过一张带超链接的图片进行XSS攻击。
- 如果你的GET请求是有副作用的,你不仅会可能被一个黑客攻击,你还可能会被一只爬虫无意的暴击。
第一个问题是URI本身的安全缺陷,而第二,三个问题其实是由于不理解GET方法,滥用GET请求导致的。我们可以容易猜到HTTP和HTML相关小组的设计思路:GET是用来获取数据的,因为获取数据是没有副作用的,所以你才可以在HTML上通过随意的一个点击操作来触发。如果你背离协议约定擅自利用GET进行含有副作用的操作,即利用GET进行实际上的不安全(Safe)操作,你将面临不安全(Security)。
从这里示例看来,POST的确比GET安全(Security)一点点,但这并不是一个明显的或者说有意义的区别,而且POST那高出一点点的安全(Security)性并不能成为你选择POST而不是GET的理由,语义区别才是。
幂等性(Idempotent)
<RFC7231>
A request method is considered "idempotent" if the intended effect on
the server of multiple identical requests with that method is the
same as the effect for a single such request. Of the request methods
defined by this specification, PUT, DELETE, and safe request methods
are idempotent.
幂等的含义是多次请求,效果一致。GET和POST的第三个重要区别,RFC文档已经写得很清楚了:
安全(Safe)方法包括GET都是幂等的,POST不是幂等的。
‘POST是非幂等的‘就是你提交完表单后,按F5后浏览器会弹框要求你重复确认是否刷新的原因。
缓存性
<RFC7231>
Request methods can be defined as "cacheable" to indicate that
responses to them are allowed to be stored for future reuse; for
specific requirements see [RFC7234]. In general, safe methods that
do not depend on a current or authoritative response are defined as
cacheable; this specification defines GET, HEAD, and POST as
cacheable, although the overwhelming majority of cache
implementations only support GET and HEAD.
GET和POST的第4个重要区别可以用一言以蔽之:
安全(Safe)方法包括GET都是可被缓存的,POST不会。
使用GET,你可能会有意无意就享受到从浏览器到代理到网络服务商再到服务器各个网络组件一层一层的透明缓存;而POST默认是不可缓存的,需要你显式使用缓存相关Header。
当然,这也只是协议的规定,具体情况还是得看各端实际实现,如果你用的某城宽带,你任何一个POST都可能被缓存。
数据传递方式
<RFC7231>
A client sends request header fields to provide more information
about the request context, make the request conditional based on the
target resource state, suggest preferred formats for the response,
supply authentication credentials, or modify the expected request
processing. These fields act as request modifiers, similar to the
parameters on a programming language method invocation.
<RFC7231>
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
由于HTTP没有定义GET报文载荷(Body段)的语义,各网络组件不保证会支持带报文Body的GET请求;另一方面header用于描述报文而不是交换的资源,因此URI是GET请求报文描述要获取资源的核心手段,一般而言URL中的路径和查询字符串(俗称GET参数)组件是GET方法是向服务器提供资源描述信息的最主要途径。
而根据RFC7231对POST的描述:
Perform resource-specific processing on the request payload
毫无疑问,请求的body段则是POST方法向服务器传递数据的途径。
因此,网上流传的某篇HTTP辟谣文里谈到的"Method和Data(URL, Body, Header)是正交的两个概念"的说法,反而是有问题的。
基于实现的区别
除了协议上的区别外,基于各端工具的实际实现,GET和POST还有一些小的差别:
-
使用载荷的POST比使用URI的GET可以传递更多数据。
HTTP协议本身没有限制传递信息的最大值,但具体的限制受各个网络组件的实现影响,一般而言,GET的长度限制更短。当然用来获取数据的GET方式,绝大多数情况也用不着传递那么多的数据。这方面的更多内容我推荐你阅读《关于 HTTP GET/POST 请求参数长度最大值的一个理解误区》 -
有些浏览器在发送POST的Ajax请求时会在发送POST请求前先发送一个HEAD,而GET请求则是直接发送。
这也是雅虎<雅虎前端优化35条规则>中提到使用Ajax时应用GET代替POST的理由,至于这条建议是否有必要,那就就见仁见智了。
以上就是我认为值得提及的GET和POST的区别。希望大家以后提到GET,POST的区别时,首先想到的是更重要的语义,幂等性,副作用和可缓存性方面的区别,而不再只是什么“使用报文体的POST比使用地址栏可见的URL的GET更安全”,“GET可能被放到收藏夹”和“POST能传的数据比GET多”这些小经验。