网站首页 > 技术文章 正文
当我们被问及 HTTP 的 GET 与 POST 两种请求方式的区别的时候,很多答案是说 GET 的数据须通过 URL 以 Query Parameter 来传送,而 POST 可以通过请求体来发送数据,所以因 URL 的受限,往往 GET 无法发送太多的字符。
这个回答好比在启用了 HTTPS 时,GET 请求 URL 中的参数仍然是明文传输的一样。
GET 果真不能通过 Request Body 来传送数据吗?
非也。
如此想法多半是因循着网页中 form 的 method 属性只有 get 与 post 两种而来。因为把 form 的 method 设置为 post,表单数据会放在 body 中,而 method 为 get(默认值) 时,提交时浏览器会把表单中的字符拼接到 action 的 URL 后作为 query parameter 传送。
于是乎就有了这么一种假像:HTTP GET 必须通过 URL 的查询参数来发送数据。
其实 HTTP 规范并未规定说 GET 就不能发送 body 数据,在 RFC GET 中只是说:
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
只是说 GET 意味着通过 URI 来识别资源。
测试
我也是本着传统上对 GET 与 POST 区别的误解很多年,今天突然意识到 GET 应该可以使用 body,况且 HTTP 本身是一个纯文本的协议。没有测试就没有 100% 的发言权,所以做了如下的测试。
在一个 Spring Boot Web 项目中创建的 GET 请求 API:
@RestController public class DemoController { @RequestMapping(value = "/", method = RequestMethod.GET) public String getRequest(@RequestParam("id") String id, @RequestBody String body) { return id + ": " + body; } }
上而创建的 GET 请求,URL 是 /?id=something,然后希望通过 request body 来获得请求数据。
再来一个测试用例,给 GET 请求发送 body 数据:
@RunWith(SpringRunner.class) @WebMvcTest public class DemoControllerTest { @Autowired private MockMvc mockMvc; @Test public void shouldReturnDefaultMessage() throws Exception { this.mockMvc.perform(get("/?id=100").content("Hello, Get Body")) .andDo(print()) .andExpect(content().string(is("100: Hello, Get Body"))); } }
14上面的单元测试顺利通过,说明对于 GET 请求我们同样可以使用 Request Body 来发送数据,而且 Spring 的测试框架也支持 GET 发送 body 数据。
再作一个验证,curl 命令, 需要用 -X 指定为 GET 请求,否则 curl 在使用 -d 发送 body 数据时自动切换为 POST 请求:
通过 curl -v 可以看到详细的请求响应数据,两个请求的 Content-Length 都是 8,即 “Get Body” 的长度,它们确实是在 Request Body 中,服务端接送 GET 来的 body 数据也没有半点问题。
下面是通过 Wireshark 捕获到的数据包的样子:
其他工具的支持情况
如果说通过 Spring 的测试用例以及 curl 命令还有所疑问的话,看上面那张图片就分明的告诉我们是在使用 GET 发送 body 数据的。
但确实有些工具或类库不让我们发送 GET 请求时设置 Body。
Postman
如著名的 Postman,在选择 GET 时 Body 标签是灰色不可用的:
而且从目前最新的 Apache Http Client 4.5 组件,它的 HttpGet 也不支持设置 Request Body, 因为 HttpGet 没有像 HttpPost 那样的 setEntity(entity) 方法。
OkHttpClient
另一个 OkHttpClient 库也不支持 GET 发送 Request Body,当执行下面的代码时:
new Request.Builder() .url("http://localhost:8080/?id=100") .method("GET", RequestBody.create(MediaType.parse("application/json"), "hello body")) .build();
直接告诉我:
java.lang.IllegalArgumentException: method GET must not have a request body
AsyncHttpClient
最后再试一个 AsyncHttpClient 库:
Dsl.asyncHttpClient() .prepareGet("http://localhost:8080/?id=100") .setBody("Get Body") .execute() .toCompletableFuture() .thenAccept(System.out::println).join();
输出 “100: Get Body”, 证明 AsyncHttpClient 是可以 GET 时发送 Body 数据的。
小结一下
Apache Http Client 和 OkHttpClient 都不支持 GET 请求发送 Body 数据,而 AsyncHttpClient 是可以的。
那么回过头来想想为什么 HTTP 并未规定不可以 GET 中发送 Body 内容,但却不少知名的工具不能用 GET 发送 Body 数据,所以大致的讲我们仍然不推荐使用 GET 携带 Body 内容,还有可能某些应用服务器也会忽略掉 GET 的 Body 数据(???,猜的)。
我想更主要是 GET 被设计来用 URI 来识别资源,如果让它的请求体中携带数据,那么通常的缓存服务便失效了,URI 不能作为缓存的 Key。
但另一方面,如果仅仅是为了读取资源,而需要使用 Body 发送一大批数据时,改用 POST 请求却与 RESTFul 的 POST 语义不相符。这时候或许可以 GET + BODY, 但是不能对该请求以 URI 作为 Key 进行缓存了。
转载自:https://yanbin.blog/why-http-get-cannot-sent-data-with-reuqest-body/,
作者:隔叶黄莺 Yanbin Blog
猜你喜欢
- 2024-10-13 谈谈springboot 获取前端json数据几种方法
- 2024-10-13 在Spring Boot中如何获取到Request对象?
- 2024-10-13 SpringBoot:如何优雅地进行响应数据封装、异常处理
- 2024-10-13 SpringBoot实现接口防抖的几种方案,杜绝重复提交
- 2024-10-13 @PostMapping @GetMapping注解 postmapping注解接收参数
- 2024-10-13 如何在SpringBoot中动态过滤JSON响应正文
- 2024-10-13 WebSocket 集群解决方案 websocket500
- 2024-10-13 SpringBoot跨系统调用接口方案 springboot跨越设置
- 2024-10-13 SpringBoot如何优雅的进行参数校验(一)
- 2024-10-13 IntelliJ IDEA必装插件以及SpringBoot使用小技巧合集
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)