HTTP请求方法的异同
HTTP(HyperText Transfer Protocol,超文本传输协议)中定义的请求方法共八种(GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE,参看RFC7231)。工作中我们最常接触到的是其中的GET、POST、PUT和DELETE。网上有很多针对这四种方法的说明和比较,但内容都不尽如人意。为了能让大家更清晰地了解这些方法,在实际工作中灵活运用,我将从定义和实现两个方面详细地阐述一下它们的异同。
首先,看一些基本概念。超文本传输协议是网络七层结构中应用层的协议。我们看到“超文本”一般会联想到另一个词:HTML(HyperText Mark-up Language,超文本标记语言)。那什么是“超文本”?“超文本”是1965年美国人Ted Nelson创造的,简单的说就是包含有其他文本链接的文本 (Hypertext is text which contains links to other texts),通过这些链接可以访问指向的文本。HTML就是用来创建超文本(如Web页面)的。最初设计HTTP的目的是为了能够在客户端和服务器端之间传输超文本文档 (HTML)。不过随着时代的发展,“超文本”已经从最初的纯文本扩展成为包括文本、图像、音频、视频等在内的多媒体。现今,把HTTP称为超媒体传输协议可能更为合适些。
针对HTTP,有文档明确定义的版本有0.9(1991)、1.0(1996)、1.1(1997)和2(2015)。目前使用最广泛的是1.1,该规范由IETF(Internet Engineering Task Force,互联网工程任务组)中的HTTP Working Group(HTTP工作组)于2014年更新,定义的内容在RFC7230–RFC7235六个文档中。
接下来,开始正式的对比:
定义上
RFC文档定义了三种方法属性:安全、幂等、可缓存。
安全
如果请求方法的语义是只读的(如GET、HEAD、OPTIONS、TRACE ),那么该方法就是安全的。也就是说客户端不会请求和期望改变服务器上目标资源的任何状态。
幂等
如果使用该方法对服务器发送多次相同请求和一次请求的效果是一样的,那么该方法就被认为是幂等的。PUT, DELETE及上面提到的安全方法都是幂等的。
可缓存
对于请求方法的响应被允许存储以备将来重用,那么该方法就可以定义为“可缓存”。一般来说,不依赖于当前响应的安全方法被定义为可缓存的。规范将GET,HEAD和POST定义为可缓存,尽管绝大多数缓存实现只支持GET和HEAD。
属性 | GET | POST | PUT | DELETE |
---|---|---|---|---|
安全 | 是 | 否 | 否 | 否 |
幂等 | 是 | 否 | 是 | 是 |
可缓存 | 是 | 是 | 否 | 否 |
语义 | 检索信息(查) | 创建或附加资源(增、改) | 创建或替换资源(增、改) | 删除资源(删) |
⚠️ 以上的“是”,指的是定义中有明确要求的。“否”指的是没有明确要求,可做可不做的。
稍微解释下:
- 安全:使用GET方法,不能修改服务器上的数据,而其它方法是可以的。
- 幂等:同样是添加资源,使用PUT只能添加一条数据,无论执行多少次。而使用POST可以是执行一次就添加一条新数据。
补充:
-
所有通用的服务器必须支持GET和HEAD方法,其他方法是可选的。
这和大部分的实现还算是比较吻合,因为一般的服务器是做网站用途的,在地址栏中访问对应的URL地址、图片引入、JS文件引入、CSS文件引入等都是发送的GET请求。 -
无论是GET还是POST都没有限制请求参数的长度和位置(URL后还是消息体message body里)。
实现上
-
GET能被缓存,POST很少能被缓存 。
-
GET 和 POST 请求参数的位置不同,GET 请求的参数放在 URL 中(以?分割URL和传输数据,参数之间以&相连,如:login?name=zqc&password=zqc。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64编码,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII),而 POST 请求的参数放在消息体中。
-
由于以上2的缘故,再加上有些服务器会记录和打印GET请求的地址,因此,POST比GET相对安全一些。但是使用抓包工具的话,只要不加密,参数都能被轻而易举地获取。
-
华为手机浏览器不支持PUT方法,因此有些公司会规定添加和修改都使用POST。
-
GET和POST请求参数的大小主要是由浏览器和Web服务器决定。以下是利用node.js做服务端进行的一系列测试(感谢 橘子君):
浏览器 | 请求类型 | GET(Bytes) | POST(Bytes) |
---|---|---|---|
IE11 | 地址栏 | 2047 | -- |
ajax | 81593 | 102400 | |
IE11 | 地址栏 | 2047 | -- |
ajax | |||
Edge | 地址栏 | 2083 | -- |
ajax | 81488 | 102400 | |
Chrome | 地址栏只粘贴 | 81550 | -- |
粘贴加输入 | 如果超32791,则为:32791+一次性新输入个数 | -- | |
ajax | 有可能是服务器端限制 81459 | 102400 | |
python | ajax | 81825 | 102400 |
也就是说除了在地址栏中输入URL发送GET请求有明显的限制,而直接通过代码发送请求,Get(80K左右)和Post(100K)发送的数据大小相差不多。
误区
- 一般认为HTTP请求方法与CRUD(Create, Read, Update, Delete)操作的对应关系如下:
而实际上,除了GET和DELETE可以这样对应,POST和PUT都可以作为增加或修改操作。
CRUD | HTTP |
---|---|
增加 | POST |
查询 | GET |
更新 | PUT |
删除 | DELETE |
- GET请求能够被保存在浏览器的浏览历史里面
不准确!只有是在浏览器地址栏内输入的URL地址才可以在浏览器历史中查看。
- GET参数是带在URL后面,传统IE中URL的最大可用长度为2048字符,其他浏览器对URL长度限制实现上有所不同,POST请求无长度限制
不准确!上方已经写出了结论:除了在地址栏中输入URL发送GET请求有明显的限制,而直接通过代码发送请求,Get(80K左右)和Post(100K)发送的数据大小相差不多。
最后,再看下HTTP状态码,简单易懂: