基础理解
编码
https://www.cnblogs.com/yuansc/p/9076604.html
Content-Type是指http/https发送信息至服务器时的内容编码类型,contentType用于表明发送数据流的类型,服务器根据编码类型使用特定的解析方式,获取数据流中的数据。
application/x-www-form-unlencoded
form表单中可以定义enctype属性,该属性的含义是在发送到服务器之前应该如何对表单数据进行编码。默认的情况下,表单数据会编码为 "application/x-www-form-unlencoded",用application/x-www-form-urlencoded编码格式的数据放到fromData里
multipart/form-data
使用表单上传文件时,必须指定表单的 enctype属性值为 multipart/form-data. 请求体被分割成多部分,每部分使用 --boundary分割,用multipart/form-data编码格式的数据放到request Payload里
application/json
当对复杂的对象进行传参时,需要用到application/json编码类型,用application/json编码格式的数据放到request Payload里
跨域
满足下面的条件即为简单请求,简单请求和非简单请求的区别是要发送一个预检(preflight)。
* 请求方式:HEAD,GET,POST
* 请求头信息:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type 对应的值是以下三个中的任意一个
application/x-www-form-urlencoded
multipart/form-data
text/plain
正文
在SpringMVC
中我们可以选择数种接受JSON
的方式,在说SpringMVC
如何接受JSON
之前,我们先聊聊什么是JSON
。具体的定义我也不赘述了,在JavaScript
中我们经常这样定义JSON
对象
var jsonObject = {
"username":"admin",
"password":123
}
这种形式的我们叫它JSON对象,同时还有一个概念叫做JSON字符串,字符串呢,顾名思义,是由' '或者" "包裹起来的一个整体,我们称之为字符串。我们知道字符串是可以直接输出的,而对象不能直接输出。所以在JavaScript中,我们可以
//定义一个对象 jsonObject
var jsonObject = {
"username":"admin",
"password":123
};
alert(jsonObject);
此时,会显示[object Object]而不会输出JSON对象的内容,JavaScript向我们提供了两个工具
JSON.parse()
用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify()
用于将 JavaScript 值转换为 JSON 字符串。
所以当我们输入
alert(JSON.stringify(jsonObject));
就会显示 {"username":"admin","password":123};
** 好了 对于JSON的讲解就到这里了 下面我们说一说SpringMVC **
既然JSON有着上述两种存在方式,那我们通过ajax向SpringMVC传值的时候,我们该传哪一种呢?
我们首先尝试直接发送JSON对象
//定义json对象
var username = $("#username").val();
var password = $("#password").val();
var json = {
"username" : username,
"password" : password
};
// Jquery Ajax请求
$.ajax({
url : "jsontest",
type : "POST",
async : true,
data : json,
dataType : 'json',
success : function(data) {
if (data.userstatus === "success") {
$("#errorMsg").remove();
} else {
if ($("#errorMsg").length <= 0) {
$("form[name=loginForm]").append(errorMsg);
}
}
}
});
我们首先想想SpringMVC提供了什么给我们,有一个@RequestParam的注解,对于这个注解,它的作用和我们Servlet
中的request.getParameter
是基本相同的。我们首先使用这个注解来获取
@RequestMapping("/jsontest")
public void test(@RequestParam(value="username",required=true) String username,
@RequestParam(value="password",required=true) String password){
System.out.println("username: " + username);
System.out.println("password: " + password);
}
后台成功输出的我们的参数,成功接受!
SpringMVC如此智能,如果我们去除@RequestParam注解,直接将两个值放入会有什么后果?
@RequestMapping("/jsontest")
public void test(String username,String password){
System.out.println("username: " + username);
System.out.println("password: " + password);
}
竟然同样成功了,原理我这里就不多赘述了,有兴趣的朋友们可以打断点看看。
SpringMVC提供了一个@RequestBody,它是用来处理前台定义发来的数据Content-Type
: 不是application/x-www-form-urlencoded编码的内容,例如application/json, application/xml等;
细心的朋友们或许发现了,在之前的Ajax中,我们没有定义Content-type的类型,Jquery默认使用application/x-www-form-urlencoded类型。那么意思就是SpringMVC的@RequestParam注解,Servlet的request.getParameter是可以接受到以这种格式传输的JSON对象的。
为什么呢!?GET请求想必大家都不陌生,它将参数以url?username="admin"&password=123这种方式发送到服务器,并且request.getParameter可以接收到这种参数,我们在浏览器地址栏上也可以看到这一点。而我们Axios使用的POST,并且发送的是JSON对象,那么后台是如何获取到的呢?答案就在于这个Content-Type
x-www-form-urlencoded的编码方式把JSON数据转换成一个字串,(username="admin"&password=123)然后把这个字串添加到url后面,用?分割,(是不是和GET方法很像),提交方式为POST时候,浏览器把数据封装到HTTP BODY中,然后发送到服务器。所以并不会显示在URL上。(这段可能有点绕口,希望大家用心理解一下。)
终于说完了,长吐一口气。所以说我们使用@RequestBody注解的时候,前台的Content-Type必须要改为application/json
,如果没有更改,前台会报错415(Unsupported Media Type)。后台日志就会报错Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported,这些错误Eclipse下Tomcat是不会显示错误信息的,只有使用了日志才会显示,如何配置日志大家可以看我上一篇文章。接下来我们正确配置一下,上面说到了 Content-Type需要更改,同时我们的data也要更改了,这种注解方式只接受JSON字符串而不是JSON对象
一定要设置axios属性,关键部分俩点application/json和将参数转换为json字符串格式
const http = axios.create({
baseURL: 'http://localhost:8080',
timeout: 5000,
headers: {
'Content-Type': 'application/json'
},
transformRequest: [function (data, headers) {
headers.token = store.state.token;
if (headers['Content-type'] === 'multipart/form-data') {
return data;
} else {
return JSON.stringify(data);
}
}]
});
this.axios({
method: 'post',
url: '/type/save',
data: {mv_id: this.mv_id, mv_name: this.mv_name}
});
后台也更改一下,json其实可以理解为键值对嘛,所以我们用Map接收,然后对字符串或者其他数据类型进行进一步处理。
@RequestMapping("/jsontest")
public void test(@RequestBody(required=true) Map<String,Object> map ){
String username = map.get("username").toString();
String password = map.get("password").toString();
System.out.println("username: " + username);
System.out.println("password: " + password);
}
同时,我又想起了神奇的SpringMVC,所以我决定去掉注解试试,好的,果断被爆了一个空指针错误...尝试就此打住。
SpringMVC还提供了参数直接和POJO绑定的方法,我们来尝试一下。前台一样,就不贴出来了。
@RequestMapping("/jsontest")
public void test(@RequestBody User user ){
String username = user.getUsername();
String password = user.getPassword();
System.out.println("username: " + username);
System.out.println("password: " + password);
}
OK,这次是可以取到值的,我个人对于登录这类小数据量的上传来说不太喜欢这种方法,User里面的变量很多,我只用了其中两个,没有必要去创建一个User对象,一般数据量小的时候我还是比较喜欢使用单独取值出来的。我们再想一想,如果是在上传JSON对象的情况下,我们可不可以绑定POJO呢,答案是可以的,不要使用@RequestParam注解,否则会报Required User parameter 'user' is not present
错误。到此讲解基本结束了,下面来总结一下。
- 我们首先说了JSON对象和JSON字符串
- 然后说了SpringMVC接受两种两种JSON格式的时候,前端ContentType的设定,和后端是否使用注解接受,还提到了一点Servlet。
- 当Ajax以application/x-www-form-urlencoded格式上传即使用JSON对象,后台需要使用@RequestParam 或者Servlet获取。 当Ajax以application/json格式上传即使用JSON字符串,后台需要使用@RquestBody获取。