get和post的区别可以从三个层面来说:浏览器/服务器层面、HTTP协议层面、语义层面。
浏览器/服务器层面
二者在浏览器层面有着多方面的不同:
- GET把参数写在URL中,POST通过body来传参。
- GET后退按钮/刷新无害,POST数据会被重新提交(浏览器应该告知用户数据会被重新提交)。
- GET书签可收藏,POST为书签不可收藏。
- GET能被缓存,POST不能缓存 。
- GET编码类型application/x-www-form-url;POST编码类型encodedapplication/x-www-form-urlencoded 或 multipart/form-data,为二进制数据使用多重编码。
- GET历史参数保留在浏览器历史中;POST参数不会保存在浏览器历史中。
- GET对数据长度有限制,当发送数据时,GET 方法向 URL 添加数据,URL 的长度是受限制的(URL 的最大长度是 2048 个字符,但并不严格是,取决于浏览器的限制,如Firefox支持10w个以上的字符);POST无限制。
- GET只允许 ASCII 字符;POST没有限制,也允许二进制数据。
- 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分;在发送密码或其他敏感信息时绝不要使用 GET ;POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。
- GET的数据在 URL 中对所有人都是可见的;POST的数据不会显示在 URL中。
- 多数服务器出于安全、稳定等多方面的考虑,会给URL长度加限制,但是这个限制是针对所有HTTP请求的,与GET、POST没有关系。
然而上面这些只是表象。我们可以说浏览器在某种程度上实现了HTTP协议,但不能说浏览器的行为就是HTTP协议的规定。
GET和POST是由HTTP协议定义的。那么使用哪个方式与应用层的数据如何传输是没有相互关系的。HTTP从没有要求POST一定要放到请求体里面,GET就一定要放到URL里面。在HTML标准中,是有着类似的定义。但这只是HTML标准对HTTP协议的用法的约定。也就是规范上说GET和POST无区别。只是因为有各种浏览器等软件的实现,就变成了我们现在熟知的现象:GET要用URL传参,POST用body传参。
HTTP协议也明确地指出了,HTTP请求头和请求体都没有长度的要求。只是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中表现出一些不同。
数据传输层面
在数据传送上,二者的区别在于Get产生一个TCP数据包;Post产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);对于POST,浏览器先发送header,服务器响应100(continue),然后再发送data,服务器响应200(返回数据)。
语义层面
前面说的都是实践或行为,而非规范,也可以说是语义。语义指明了行为的性质或意义。比如GET的语义就是「获取资源」,POST的语义是「处理资源」,那么在具体实现这两个方法时,就必须考虑其语义,做出符合其语义的行为。
- GET的语义是请求获取指定的资源。GET方法是安全的、幂等的、可缓存的(除非有 Cache-ControlHeader的约束),GET方法的报文主体没有任何语义,即它除了返回结果不应该会产生其它副作用(如写数据库)。GET请求可以不交由web服务器处理,直接走CDN缓存;
- POST的语义是根据请求负荷(报文主体)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST请求不安全,非幂等,(大部分实现)不可缓存,通常必须交由web服务器处理。
解释几个名词
安全
这里的「安全」和通常理解的「安全」意义不同,如果一个方法的语义在本质上是「只读」的,那么这个方法就是安全的。客户端向服务端的资源发起的请求如果使用了是安全的方法,就不应该引起服务端任何的状态变化,因此也是无害的。 此RFC定义,GET, HEAD, OPTIONS 和 TRACE 这几个方法是安全的。但是这个定义只是规范,并不能保证方法的实现也是安全的,服务端的实现可能会不符合方法语义,正如上文说过的使用GET修改用户信息的情况。引入安全这个概念的目的是为了方便网络爬虫和缓存,以免调用或者缓存某些不安全方法时引起某些意外的后果。User Agent(浏览器)应该在执行安全和不安全方法时做出区分对待,并给用户以提示。
幂等
幂等的概念是指同一个请求方法执行多次和仅执行一次的效果完全相同。按照RFC规范,PUT,DELETE和安全方法都是幂等的。同样,这也仅仅是规范,服务端实现是否幂等是无法确保的。引入幂等主要是为了处理同一个请求重复发送的情况,比如在请求响应前失去连接,如果方法是幂等的,就可以放心地重发一次请求。这也是浏览器在后退/刷新时遇到POST会给用户提示的原因:POST语义不是幂等的,重复请求可能会带来意想不到的后果。
可缓存性
顾名思义就是一个方法是否可以被缓存,此RFC里GET,HEAD和某些情况下的POST都是可缓存的,但是绝大多数的浏览器的实现里仅仅支持GET和HEAD。关于缓存的更多内容可以去看RFC7234。