HTTP中GET和POST的区别
1. 缘起
老实说我对get和post的区别已经完全糊涂了。糊涂的原因大概因为遇到一些奇怪的情况:
- 看到很多人说,get用来获取资源,post用来新建资源。但是根据最近在做爬虫的经验,登陆请求一般都用的是post,可是登陆的时候新建了什么资源呢?况且返回的状态码都是200,怎么看没有新建一个资源吧。
- 在学习python的requests库的过程中,在v0.5.0之前post请求返回的status_code是201,但是v0.5.0开始全部(即使是上传文件)换成了200,但是大神在commit里面并没有说明原因。我自己请求了一下网站返回的结果确实是200。大神在干啥。。
这两个案例都让我很迷茫,如果post是用来新建资源的,那么首先登陆不应该用post,其次post请求返回的结果应该是201,于是啃哧啃哧的开始翻网上的文章。
2. Restful架构
后来才知道,如果要解释上述的两个问题,其实一句话就说明白了,get用来获取资源,post用来新建资源,这是在restful架构下的设计,这个架构他只是一种风格,而非一种规定。 于是回到刚才的问题。
- 登陆请求是restful架构的吗?显然不是,restful的url是要对应一个资源的,
http://xxx.com/login
,login显然不是一个资源,而是一个动作。另外这个请求也没有创建一个资源。 - requests用来测试的网站
https://httpbin.org
真的只是一个用来测试的网站,并没有说遵从restful架构,他想返回什么就返回什么。
哦原来如此,现在我们可以抛开Rest来谈区别了。
3. GET和POST的区别
Restful是在http之上的一层概念,那么我们回到http的本来面目,get和post的区别到底什么呢?比如为什么一般登陆都是用post请求呢?总结了一些二手的观点。
3.1 直观区别
rfc2616 中说到:The GET method means retrieve whatever information ([…]) is identified by the Request-URI 。也就是说:
- GET请求只从URL的参数中获取数据
- POST可以同时从参数和body中获取数据(POST从参数中获取数据好不好另说,可见这篇文章)。
一个比较有意思的地方是,request库在v0.5.0之前是不支持post传递params参数的,详见80d860d, f31ade3, 再后来似乎get也让传body了?后面看了再来补充。
3.2衍生区别
正是由于上面所说的这个区别,导致GET和POST在应用过程中体现出一些不同:
- 浏览器对URL的长度有限制,所以GET不能代替POST发送大量数据。RFC2616中对URL长度没有规定,但是浏览器比如ie和chrome一般都会限制。
- GET方法应该是幂等的。幂等啥意思,就是说多次请求和一次请求的效果是一样的。如果用GET请求新建资源,可能会造成爬虫爬一次就新建一个资源的后果。所以发送表单如果正在loading, 刷新一下的时候浏览器会提醒你会重复发送表单数据。
- 把数据裸露在URL中使GET更加不安全。这里的安全是相对的,反正抓个包数据都能看见,因为浏览器会记录URL历史,一般网络服务商也只记录URL,在刚才的登陆例子中,如果是用GET方法发送登陆请求,那么只要你在别人电脑里登陆了一下,别人哪怕没什么技术能力,也能轻松看到你的密码。
- GET请求能被缓存,POST请求不能被缓存。
- GET只允许 ASCII 字符,POST没有限制。也允许二进制数据。
3.3 本质区别
V2EX上看到一个大哥说,GET和POST的区别就是,一个的方法是’GET’,一个的方法是’POST’。我觉得这个答案很有哲理性..很有佛教性空的味道。
就是说:在http这个应用层协议上来说,两种被定义为不同的方法,但是从TCP这个传输层上说,他们并没有什么区别。给GET加上request body,给POST带上url参数?完全可以。