application/x-www-form-urlencoded与multipart/form-data与application/json的区别 精析

1.起因

  在网上找不到能够将application/x-www-form-urlencoded与multipart/form-data与application/json三者区别完全解释清楚的文章,真是令人失望,特发此帖详细解说。

  前端数据传递至后台时,需要对其进行编码,其中,编码格式可分为四种:application/x-www-form-urlencoded,multipart/form-data,application/json,text/plain。

  text/plain是纯文本数据,这里不做解释,平常也没人会使用。

  前后台完成数据交互的方式只有两种:一是form表单提交,二是ajax提交。

  form表单可通过enctype属性设置编码类型,默认值为:application/x-www-form-urlencoded;ajax可通过contentType属性设置编码类型,默认值也是:application/x-www-form-urlencoded;

2.application/x-www-form-urlencoded

  后台如何接收?使用request.getParameter("work");来获取参数名和参数值。

  这种编码格式,是我们最常见的一种方式,将数据封装成一个字符串,参数名和参数值使用"="拼接,参数之间使用"&"拼接,最终传递至后台的数据格式形如:key1=value1&key2=value2&...;

  另外,key和value都会分别使用encodeURI()对其进行编码,也就是你所要传递的数据,实际上已经进行了一次编码,形如:name=Marydon&work=%E7%A8%8B%E5%BA%8F%E5%91%98,服务器接收到后干的第一件事就是使用URLDdecoder.decode()对name和value进行一次解码。

  不管是form表单请求还是ajax请求,都是这样进行数据组装的。

  get请求与post请求的基本区别:

  这是get请求

  下图是post请求

  区别在于:

  get请求:没有请求体;会直接将form数据拼接到url中,用?隔开,数据对外可见;

  post请求:将数据放到请求体中;无法直观看到要传输的数据,安全性相对高一点。

  PS:想要比较两种请求的区别,在IE浏览器下调试最直观,chrome不行。

  这是为我们大众所熟知的两个区别,那么问题来了,为何get请求提交的数据携带中文时,后台接收到的中文数据会乱码,而post请求就不会乱码呢?

  再来看一下这两个请求:  

  get请求

  post请求

  后台将请求字符集打印出来

// 该请求的字符集是
System.out.println(request.getCharacterEncoding());  

  通过前后端测试,这次我们可以看出来get请求与post请求的另一个区别:

  当form表单数据编码类型设置为application/x-www-form-urlencoded时(也就是默认值),浏览器向后台发送请求时,分为两种情况:

  当请求方式为get时,请求头部信息没有Content-Type属性,也没有指定数据的字符集;

  当请求方式为post时,请求头部信息有Content-Type属性,并指定数据的字符集,即:application/x-www-form-urlencoded; charset=UTF-8;

  那么get请求时,所采用的字符集到底是什么?通过后台测试得出结果:ISO-8859-1

// get请求默认字符集
String work = new String(work.getBytes("iso-8859-1"), "utf-8");

  那么既然如此,在ajax请求中,我们是不是可以显式声明contentType属性,解决get请求乱码问题呢?

type : 'get',// 请求方式
contentType : 'application/x-www-form-urlencoded; charset=UTF-8',// 显式声明

  网页已经生效

  结果却令人失望:

  还是乱码,还得重新编码。 

  Content-Type属性,告诉服务器提交的数据的字符集是utf-8,并让服务器以utf-8格式解析数据,由此可见:

  问题不在于服务器,而在于浏览器:当浏览器检测到编码格式为application/x-www-form-urlencoded并且为get请求时,浏览器会先用ISO-8859-1对form数据进行编码,然后再用encodeURI()对其进行编码。

  所以,get请求导致后台接收到的中文出现乱码的根本原因是:页面字符集是utf-8,浏览器却按iso-8859-1进行重新编码,后台接收后以utf-8进行解码当然会出现乱码。

  小结:

  当表单数据的编码格式为:application/x-www-form-urlencoded时:

  get请求,浏览器会将数据按照iso-8859-1进行重新编码,导致后台接收到中文时必然乱码;

  post请求,浏览器会按照utf-8对要提交的数据进行编码,由于后台本身就是使用utf-8对其进行解析,所以才不会出现乱码的情况。

  至于get请求解决乱码问题主要有两种方式,具体见另一篇文章。

3.multipart/form-data

  主要用于传输文件,将文件转换成二进制数据进行传输,不涉及转码问题。

  后台如何接收?使用request.getInputStream();取值。

  IE浏览器和chrome浏览器下,传输的form数据是不一样的;

  IE浏览器

  chrome浏览器 

  共同点是:Content-Type的值为multipart/form-data; boundary=--...,貌似没有其它的固定格式,不再考虑,后台只要能接收到就行。

  后台接收到的数据,形如:

  结合代码进行实现: 

  前端发送数据

<input type="file" id="file" onchange="upload('getParams')" style="display: none;">
<input type="button" value="上传" onclick='javascript:$("#file").click()'>  
function upload(url) {
    // js 获取文件对象
    var fileObj = document.getElementById("file").files[0];
    if (null == fileObj) {
        alert("图像上传失败,请重试!");
        return;
    }
    // 创建form表单
    var formFile = new FormData();
    // 加入文件对象
    formFile.append("file", fileObj);
    // 创建XMLHttpRequest对象
    var xhr = new XMLHttpRequest();
    // post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。
    xhr.open("post", url, true);
    //请求完成
    xhr.onload = function () {
        // 将返回数据转换成JSON对象
        var resData = JSON.parse(this.responseText);
        document.getElementById("file").value = "";
    };
    // 请求失败
    xhr.onerror = null;
    // 上传进度调用方法(可实现上传进度条)
    xhr.upload.onprogress = null;
    // //开始上传,发送form数据(以二进制数据传递给后台)
    xhr.send(formFile);
}

  后台接收

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    InputStream is = request.getInputStream();
    int i = 0;
    while ((i = is.read()) != -1) {
        System.out.print((char) i);
    }
}

4.application/json

  后台如何接收?使用request.getReader()取值,也可以使用request.getInputStream()获取。

  错误示例:

$.ajax({
	type : 'post',// 请求方式
	url : 'getParams',// 请求地址
	contentType : 'application/json; charset=UTF-8',
	data :{'name' : 'Marydon','work' : '程序员'},
	success : function(result){// 请求成功
		alert(result.work);
	}
});

  结果提交的数据还是form表单形式,根本不是json

  以json格式进行数据的传输,从前端发送json形式的方式不常见,常见的是从服务器对服务器,即一台服务器发送json数据,另一台服务器负责接收。

  言归正传,怎样才能通过ajax向后台传输json数据呢?

  首先需要明白的是,前端向后台传输数据有且只有两种方式,一种是常见的字符串格式,另一种就是二进制数据,也就是不管你向后台传输什么类型的数据都会被转换成字符串或者二进制。

  这样,我们就明白了,向后台传输,并不是传递一个json对象,而是应该传一个json字符串。

  与普通的Ajax调用的区别,仅仅在于:参数名,也就是name需要加上单引号,{}两边要加上双引号,这样就不需要使用反斜杠\来转义啦。

  代码实现

$.ajax({
	type : 'post',// 请求方式
	url : 'getParams',// 请求地址
	contentType : 'application/json; charset=UTF-8',// 告知服务器,传递的是json数据(可以省略)
	data :"{'name' : 'Marydon','work' : '程序员'}",// json字符串(必要条件)
	success : function(result){// 请求成功
		alert(result.work);
	}
});  

  后台接收:有两种方式

  方式一:使用字符流取值-request.getReader()

StringBuffer buffer = new StringBuffer();
BufferedReader reader = request.getReader();
String s = "";
while ((s = reader.readLine()) != null) {
    buffer.append(s);
}
System.out.println(JSONObject.fromObject(buffer.toString()));
System.out.println(buffer.toString());  

  方式二:使用字节流取值-request.getInputStream()

int i = 0;
StringBuffer buffer = new StringBuffer();
InputStream stream = request.getInputStream();
while ((i = stream.read()) != -1) {
    buffer.append((char)i);
}
System.out.println(JSONObject.fromObject(buffer.toString()));
System.out.println(buffer.toString());

  打印结果

  经测试发现,即使Ajax没有声明contentType,也就是采用的默认值:application/x-www-form-urlencoded,当你实际传输的是json字符串时,后台照样能够正常接收。如下图所示:

  虽然后台可以正常接收,但是,为了规范行事,还是声明上吧。 

  另外,如你所见,当想后台传递json字符串时,就会犯get请求一样的毛病:当参数值为中文时,后台接收到的将是乱码数据,需要手动进行转码。

  也就是,当ajax向后台发送json格式的数据时,即使是post请求,也会造成乱码问题。

  乱码解决有两种方式,这里不再赘述,下面文章有详细解说。 

  不管是form表单提交还是ajax提交,最常用的编码格式还是application/x-www-form-urlencoded。

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

 

posted @ 2020-04-03 16:49  Marydon  阅读(8121)  评论(0编辑  收藏  举报