POST请求和GET请求如何传递和接收解析参数
1. 前言
接口参数应该怎么传递是每个项目应该面对的问题,这跟编程语言无关,今天来总结一波常用的接口参数传递方式。
2. GET 请求
GET 请求一般用来向服务器请求获取数据。总体来说目前有两种传参风格类型。
多参数拼接
这是我们最常见的传递方式,它一般传参方式如下:
GET /api/v1/user?name=felord.cn&age=18 HTTP/1.1
Host: localhost:8080
规则为:参数和 URI 之间用问号?
隔开, 参数键值用等号=
连接,然后参数之间用连接符&
拼接起来,如样例中的name=felord.cn&age=18
。在 Spring MVC 中这种参数可以被对象接收:
@GetMapping("/user")
public Rest<?> urlEncode(User params) {
// params.name = felord.cn
// params.age = 18
return RestBody.okData(params);
}
通过
HttpServletRequest#getParameter(String key)
也获取上述方式携带的参数,但是一般这种方式建议在 Servlet Filter 过滤器使用,而不建议在接口中使用。Spring MVC 拦截器获取参数的底层也是该方式实现的。但是我也发现很多人在接口中使用此不合理的方式。 另外 URL 的长度是有限制的,如果 GET 请求包含了过于复杂的参数组合,说明业务设计是有问题的。
路径标识参数
还有一种方式就是路径参数,这种参数是期望获取数据的标识,一般为数据的唯一标识或者分页参数。例如查询标识为1
的用户:
GET /api/v1/user/1 HTTP/1.1
Host: localhost:8080
查询第1
页(每页10
条)的用户:
GET /api/v1/users/1/10 HTTP/1.1
Host: localhost:8080
在 Spring MVC 中这种参数需要通过@PathVariable
来处理:
@GetMapping("/user/{userId}")
public Rest<?> urlEncode(@PathVariable("userId") String userId) {
return RestBody.okData(userId);
}
DELETE 请求也推荐使用路径参数
3. POST/PUT 请求
GET 请求是从服务端获取数据的,而 POST 请求则是向服务端发送数据。很多不清楚它们之间区别的同学会混用它们。我见过使用 GET 请求修改数据的,也见过使用 POST 请求来查询结果的。虽然它们可以完成期望的工作但是它们之间还是有很大的差别的,这里不得不重复一下:
- GET 请求可以直接在浏览器直接请求当然也会保留在浏览器历史记录里,而 POST 不可以。
- GET 请求是天然幂等性的,而 POST 不是。
- GET 请求会被浏览器主动缓存,而 POST 不会,除非手动设置。
- GET 请求只能进行 URI 编码,而 POST 支持多种编码方式。
- 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制。
- GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
- GET 参数通过 URL 传递,而且是有长度限制的,POST 放在请求体中,没有长度限制。
GET 请求会把请求头和 DATA 一并发送出去,然后服务器响应;而对于 POST 请求会先发送请求头告诉服务器请求的编码方式等等,然后服务器响应 100 continue 后客户端再把编码后的 DATA 发送给服务器,由服务器作出响应。另外如果不使用 HTTPS,POST 请求也无法保证数据的安全传输。
表单提交
POST 请求最长使用的场景是表单提交,比如登录:
<form action="/login" method="POST">
First name: <input type="text" name="username"><br>
Last name: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
这种方式是 POST 的默认方式,Content-Type
为application/x-www-form-urlencoded
。样例如下:
POST /login HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
username=felord.cn&password=felord.cn
参数的组织方式参考 GET 请求,但是不是放在 URL 中而是放在请求体中,另外必须显式声明Content-Type
为application/x-www-form-urlencoded
。Spring MVC 中我们可以直接使用对象来接收:
@PostMapping("/login")
public Rest<?> doLogin(LoginDTO params) {
// params.username = felord.cn
// params.password = felord.cn
return RestBody.okData(params);
}
上面的登录也可以使用multipart/form-data
方式来请求:
POST /login HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"
felord.cn
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="password"
felord.cn
----WebKitFormBoundary7MA4YWxkTrZu0gW
multipart/form-data
将表单中的每个input
转为了一个由boundary
分割的小格式,没有转码,直接将 UTF-8 字节拼接到请求体中,在本地有多少字节实际就发送多少字节,极大提高了效率,适合传输长字节,通常我们用来上传文件等大字节。例如我们将路径C:/Users/felord/Desktop/spring-security.pdf
文件以myFile
为标识名上传到服务器:
POST /foo/upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="myFile"; filename="/C:/Users/felord/Desktop/spring-security.pdf"
Content-Type: application/pdf
(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW
对应的 Spring MVC 接口为:
@PostMapping("/upload")
public Rest<?> urlEncode(@RequestPart("myFile") MultipartFile file) {
String originalFilename = file.getOriginalFilename();
return RestBody.okData(originalFilename);
}
请注意 Spring MVC 文件大小默认是10485760bytes。
Ajax POST 请求
目前大部分都是前后端分离了,所以除了上传之外的 POST 请求更多建议将数据使用JSON的形式提交给服务器。当我们需要新增一个name
为felord
、age
为18
的User
时建议这么做:
POST /user/add HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"name": "felord",
"age": 18
}
将参数封装为JSON并放入请求体提交给后端。
@PostMapping("/user/add")
public Rest<?> add(@RequestBody User user) {
return RestBody.okData(user);
}
4. 总结
本文的目的希望在于明确 GET 和 POST 的使用场景和传参方式,来帮助你正确使用它们,避免混乱的方式带来的参数传递和解析问题。好了今天就到这里,多多关注:码农小胖哥,获取更多编程干货。
- 一个关于ConfigurationManager.GetSecion方法的小问题
- 追踪记录每笔业务操作数据改变的利器——SQLCDC
- 一个完整的用于追踪数据改变的解决方案
- C# 4.0新特性-"协变"与"逆变"以及背后的编程思想
- 通过内存分析工具来证明字符串驻留机制
- 如果在BackgroundWorker运行过程中关闭窗体…
- 从数据到代码——基于T4的代码生成方式
- 解决T4模板的程序集引用的五种方案
- 编写T4模板进行代码生成无法避免的两个话题:"Assembly Locking"&"Debug"
- 从yield关键字看IEnumerable和Collection的区别
- 让“链式调用(方法链)”更加自然一点
- ASP.NET应用下基于SessionState的“状态编程框架”解决方案
- 为自定义配置的编辑提供”智能感知”的支持
- 在ASP.NET Core应用中如何设置和获取与执行环境相关的信息?
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法