首发:公众号《赵侠客》
前言
做了多年的后端开发,给前端提供过的接口没有1万个也有1000多个,对接过的前端开发人员没有100人也是50人,联调过的前端开发人员从刚毕业的新人到工作过10多年的老程序员,从江南萌妹到东北壮汉几乎什么样的人都遇到过,在与前端对接过程中一直都会遇到一个问题,就是我们后端接口提供好了,自测也通过了,前端却说接口不通,当我们去排查时发现大都不是接口不通,很多情况是前端使用的姿势不对,比如接口明明写的参数是放到ULR
路径上,前端却传到了queryString
上,接口明明写的是使用application/x-www-form-urlencoded
格式,前端却传了application/json
格式,所以本文总结常用的前后端传参数据格式,方便前端与后端开发人员更好的理解HTTP接口协议传参格式。
一、通过HTTP URL传参数
这种方式是最简单,也是最常用的传参方式,通常用于前端从后端获取数据,通过URL传参也分为两种,一种是将参数放在URL
路径上,另一种是将参数放在QueryString
上,也就是URL
的?
后面
HTTP报文格式
GET /user/1?userName=admin&arrays=a,b,c,d&ids=1&ids=2&ids=3&ids=4
注意事项:
/user/1
上的1
是通过URL
路径传参数,这种RestFul
风格的传参方式,有些前端会搞错userName=admin
这种就是简单的QueryString
传参,是最常见的,一般不会搞错arrays=a,b,c,d
这种是通过QueryString
传数组,其实就是使用,
分隔ids=1&ids=2&ids=3&ids=4
这种也是传数组参数的一种方式,一般用的比较少,容易出错后端接口代码
我们使用SpringMVC
框架编写接口,可以通过@PathVariable
和@RequestParam
两个注解来接收上面参数,主要有三种方法:
Map
接收来参数;UserDTO
对象来接收。
@GetMapping("/user/{id}")
public UserDTO request(@PathVariable Long id, String userName, String[] arrays,@RequestParam List ids) {
return UserDTO.builder().id(id).build();
}
@GetMapping("/user2/{id}")
public Map user2(@PathVariable Long id,@RequestParam Map map) {
return map;
}
@GetMapping("/user3/{id}")
public UserDTO user3(@PathVariable Long id, UserDTO user) {
return user;
}
@Data
public class UserDTO {
private Long id;
private String userName;
private String[] arrays;
private List ids;
}
注意事项:
String[]
和List
两种数据类型;Map map
接收参数时 Value
的类型要是Object
类型,并且增加@RequestParam
User
对象接收参数时不要增加@RequestParam
注解前端调用接口代码
前端对于这种传参数方式直接把所有参数拼接到URL上
就好了
var request = new XMLHttpRequest();
request.open('GET', 'http://localhost/user/1?userName=admin&arrays=a,b,c,d&ids=1&ids=2&ids=3&ids=4', true);
request.responseType = 'json';
request.onload = function () {
var data = this.response;
console.log(data);
};
request.send();
注意事项:
URL
安全的需要进行URLEncode
POST
、PUT
、DELETE
方法也是支持通过URL
传参的URL
拼接不同浏览器支持的最大参数长度是不一样的,以下是不同浏览器支持参数的最大长度:浏览器 | URL长度限制 |
---|---|
IE浏览器 | 2048字节 |
360极速浏览器 | 65536字节 |
Firefox(Browser) | 2118字节 |
Safari(Browser) | 80000字节 |
Opera(Browser) | 190000字节 |
Google(chrome) | 8182字节 |
二、通过HTTP Body传参数
通过HTTP Body
传参数主要用于前端向服务端提交数据,如添加数据、修改数据、上传文件等等,通过Body
传参常用的数据格式主要有以下3种:
application/x-www-form-urlencoded
也就是表单提交,body
报文中使用key=value
拼接参数;application/json
将数据转成JSON
格式放在Body
中;multipart/form-data
用于文件上传。HTTP报文格式
application/x-www-form-urlencoded
格式报文:
POST /user3/1
Content-Type: application/x-www-form-urlencoded
userName=admin&arrays=a,b,c,d&ids=1&ids=2&ids=3&ids=4
application/json
格式报文:
GET /user4/1
Content-Type: application/json
{
"id": 1,
"userName": "admin",
"arrays": [
"a",
"b",
"c",
"d"
],
"ids": [
1,
2,
3,
4
]
}
注意事项:
GET
方法也可以通过Body
传参数,这点很多人会觉得GET方法不能通过Body
传参,不过只能传application/json
,使用过elasticsearch
应该知道,在搜索数据是就是通过GET
方法传JSON
数据的。后端接口代码
在SpringMvc
框架中接收Body
的application/x-www-form-urlencoded
类型参数和在URL
的QueryString
传参数是通用的,接收application/json
需要使用@RequestBody
注解。
@RequestMapping("/user3/{id}")
public UserDTO user3(@PathVariable Long id, UserDTO user) {
return user;
}
@RequestMapping("/user4/{id}")
public UserDTO user4(@PathVariable Long id,@RequestBody UserDTO user) {
return user;
}
@RequestMapping("/user5/{id}")
public UserDTO user4(@PathVariable Long id,@RequestBody String user) {
return JSONUtil.toBean(user,UserDTO.class);
}
注意事项:
@RequestBody
注解可以直接使用DTO
来接收,也可以使用String
来接收在手动转成DTO
,这个方法在不知道要接收的数据有哪些字段时非常有用,可以将对方传的完整数据打印出来。前端调用接口代码
function sendFormUrl() {
var request = new XMLHttpRequest();
request.open('POST', 'http://localhost/user3/1', true);
request.responseType = 'json';
request.onload = function () {
console.log(this.response);
};
var formData = new FormData();
formData.append('userName', "admin");
formData.append('arrays', "a,b,c,d");
formData.append('ids', "1");
formData.append('ids', "2");
formData.append('ids', "3");
formData.append('ids', "4");
request.send(formData);
}
function sendJson() {
var request = new XMLHttpRequest();
request.open('POST', 'http://localhost/user4/1', true);
request.responseType = 'json';
request.setRequestHeader("Content-Type", "application/json;");
request.onload = function () {
console.log(this.response);
};
var body = {
"userName": "admin",
"arrays": [
"a",
"b",
"c",
"d"
],
"ids": [
1,
2,
3,
4
]
}
request.send(JSON.stringify(body));
}
注意事项:
multipart/form-data
主要用于文件上传,可以参数我的另一篇文章:《一个Demo搞定前后端大文件分片上传、断点续传、秒传》
通过Header传参数
通过Header
传参主要用于一些通用的用户认证信息,比如常用的 Authentication: Bearer
、Cookie
等
HTTP报文格式
GET /user7/1
Accept: application/json
userName : admin
Cookie: userName=admin;
arrays: a,b,c,d
ids:1
ids:2
ids:3
ids:4
注意事项
java.lang.IllegalArgumentException: The HTTP header line [username : admin] does not conform to RFC 7230. The request has been rejected.
因为userName
这些自定义请求头不符合
RFC 7230
标准所以被拒绝了,增加配置 reject-illegal-header: off
可解决application.yml
中增加:
server:
port: 80
tomcat:
reject-illegal-header: off
后端接口代码
获取Header
参数可以通过request.getHeader(header)
依次获取,也可以通过@CookieValue
,@RequestHeader
来获取
@RequestMapping("/user6/{id}")
public User user6(@PathVariable Long id, HttpServletRequest request) {
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()){
String header= headerNames.nextElement();
log.info("{}->{}",header,request.getHeader(header));
}
return User.builder().id(id).build();
}
@RequestMapping("/user7/{id}")
public User user7(@PathVariable Long id, @CookieValue String userName, @RequestHeader String[] arrays, @RequestHeader List ids) {
return User.builder().id(id).userName(userName).arrays(arrays).ids(ids).build();
}
前端调用接口代码
function sendHeader() {
var request = new XMLHttpRequest();
request.open('GET', 'http://localhost/user7/1', true);
request.responseType = 'json';
request.setRequestHeader("arrays","a,b,c,d");
request.setRequestHeader("ids","1");
request.setRequestHeader("ids","2");
request.setRequestHeader("ids","3");
request.setRequestHeader("ids","4");
request.onload = function () {
console.log(this.response);
};
request.send();
}
注意事项:
Cookie
是浏览器自动添加了,不需要通过request.setRequestHeader("userName","admin")
添加总结
本文总结了前后端通过HTTP接口协议传参的常用方法,并从HTTP协议、后端JAVA代码、前端JS代码演示每种参数的报文格式、后端获取方法和前端调用方法,当然还有一些更高级的传参方式,比如websocket
、sse
等基于HTTP
的实时推送协议。