https://blog.csdn.net/qq_42563079/article/details/102931392
如果通过<form method=post> 直接提交数据,数据包裹在Form Data中;通过AJAX 提交,并且设置Content-Type:application/json,数据就包裹在Request Payload中。
如果url中有参数,url的参数出现在Query String Parameters中。
=============
同样是post
enctype="application/x-www-form-urlencoded" 和 enctype="multipart/form-data" 的区别在于 From Data 的格式不同
其中multipart/form-data更稳定,但部分语言(例如php)解码麻烦一些,不能直接获取。
------------
旧版浏览器的<form>标签可以支持 enctype="application/json",但新的版本w3c废弃了enctype="application/json"特性。(https://segmentfault.com/q/1010000011901619)
如果使用AJAX原生POST请求,请求头里设置Content-Type:application/json,请求的参数就会显示在Request Payload中,参数格式为JSON格式:{"key":"value","key":"value"...},这种方式可读性会更好。
https://blog.csdn.net/qq_42563079/article/details/102931392
链接:https://www.jianshu.com/p/53b5bd0f1d44
来源:简书
在常见业务开发中,POST请求常常在这些地方使用:前端表单提交时、调用接口代码时和使用Postman测试接口时。我们下面来一一了解:
一、前端表单提交时
application/x-www-form-urlencoded
表单代码:
<form action="http://localhost:8888/task/" method="POST">
First name: <input type="text" name="firstName" value="Mickey&"><br>
Last name: <input type="text" name="lastName" value="Mouse "><br>
<input type="submit" value="提交">
</form>
通过测试发现可以正常访问接口,在Chrome的开发者工具中可以看出,表单上传编码格式为application/x-www-form-urlencoded
(Request Headers中),参数的格式为key=value&key=value
。
我们可以看出,服务器知道参数用符号&
间隔,如果参数值中需要&
,则必须对其进行编码。编码格式就是application/x-www-form-urlencoded
(将键值对的参数用&连接起来,如果有空格,将空格转换为+
加号;有特殊符号,将特殊符号转换为ASCII HEX
值)。
application/x-www-form-urlencoded
是浏览器默认的编码格式。对于Get请求,是将参数转换?key=value&key=value
格式,连接到url后
ps:可以在这个网址测试表单:http://www.runoob.com/try/try.php?filename=tryhtml_form_submit
multipart/form-data
那么当服务器使用multipart/form-data
接收POST请求时,服务器怎么知道每个参数的开始位置和结束位置呢?
<form action="http://localhost:8888/task/" method="POST" enctype="multipart/form-data">
First name: <input type="text" name="firstName" value="Mickey&"><br>
Last name: <input type="text" name="lastName" value="Mouse "><br>
<input type="submit" value="提交">
</form>
我们在开发者工具中可以看出multipart/form-data
不会对参数编码,使用的boundary
(分割线),相当于&
,boundary
的值是----Web**AJv3
。
文件上传
上传文件也要指定编码格式为multipart/form-data
。
<form action="http://localhost:8888/testFile" enctype="multipart/form-data" method="POST">
<input type="file" name="file">
<input type="submit" value="提交">
</form>
如果是SpringMVC项目,要服务器能接受multipart/form-data
类型参数,还要在spring上下文配置以下内容,SpringBoot项目则不需要。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
</bean>
我们可以通过FormData对象模拟表单提交,用原始的XMLHttpRequest来发送数据,让我们可以在Chrome开发工具中查看到具体格式:
<form id="form">
First name: <input type="text" name="firstName" value="Mickey"><br>
Last name: <input type="text" name="lastName" value="Mouse"><br>
<input type="file" name="file"><br>
</form>
<button onclick="submitForm()">提交</button>
<script>
function submitForm() {
var formElement = document.getElementById("form");
var xhr = new XMLHttpRequest();
xhr.open("POST", "/task/testFile");
xhr.send(new FormData(formElement));
}
</script>
格式如下:
二、调用接口代码时
1、在代码中使用application/x-www-form-urlencoded
编码格式设置Request属性调用接口,可以如下实现:
private static String doPost(String strUrl, String content) {
String result = "";
try {
URL url = new URL(strUrl);
//通过调用url.openConnection()来获得一个新的URLConnection对象,并且将其结果强制转换为HttpURLConnection.
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
//设置连接的超时值为30000毫秒,超时将抛出SocketTimeoutException异常
urlConnection.setConnectTimeout(30000);
//设置读取的超时值为30000毫秒,超时将抛出SocketTimeoutException异常
urlConnection.setReadTimeout(30000);
//将url连接用于输出,这样才能使用getOutputStream()。getOutputStream()返回的输出流用于传输数据
urlConnection.setDoOutput(true);
//设置通用请求属性为默认浏览器编码类型
urlConnection.setRequestProperty("content-type", "application/x-www-form-urlencoded");
//getOutputStream()返回的输出流,用于写入参数数据。
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.getBytes());
outputStream.flush();
outputStream.close();
//此时将调用接口方法。getInputStream()返回的输入流可以读取返回的数据。
InputStream inputStream = urlConnection.getInputStream();
byte[] data = new byte[1024];
StringBuilder sb = new StringBuilder();
//inputStream每次就会将读取1024个byte到data中,当inputSteam中没有数据时,inputStream.read(data)值为-1
while (inputStream.read(data) != -1) {
String s = new String(data, Charset.forName("utf-8"));
sb.append(s);
}
result = sb.toString();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
String str = doPost("http://localhost:8888/task/", "firstName=Mickey%26&lastName=Mouse ");
System.out.println(str);
}
2、在代码中使用multipart/form-data
编码格式设置Request属性调用接口时,其中boundary
的值可以在设置Content-Type时指定,让服务器知道如何拆分它接受的参数。通过以下代码的调用接口:
private static String doPost(String strUrl, Map<String, String> params, String boundary) {
String result = "";
try {
URL url = new URL(strUrl);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setConnectTimeout(30000);
urlConnection.setReadTimeout(30000);
urlConnection.setDoOutput(true);
//设置通用请求属性为multipart/form-data
urlConnection.setRequestProperty("content-type", "multipart/form-data;boundary=" + boundary);
DataOutputStream dataOutputStream = new DataOutputStream(urlConnection.getOutputStream());
for (String key : params.keySet()) {
String value = params.get(key);
//注意!此处是\r(回车:将当前位置移到本行开头)、\n(换行:将当前位置移到下行开头)要一起使用
dataOutputStream.writeBytes("--" + boundary + "\r\n");
dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + encode(key) + "\"\r\n");
dataOutputStream.writeBytes("\r\n");
dataOutputStream.writeBytes(encode(value) + "\r\n");
}
//最后一个分隔符的结尾后面要跟"--"
dataOutputStream.writeBytes("--" + boundary + "--");
dataOutputStream.flush();
dataOutputStream.close();
InputStream inputStream = urlConnection.getInputStream();
byte[] data = new byte[1024];
StringBuilder sb = new StringBuilder();
while (inputStream.read(data) != -1) {
String s = new String(data, Charset.forName("utf-8"));
sb.append(s);
}
result = sb.toString();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private static String encode(String value) throws UnsupportedEncodingException {
return URLEncoder.encode(value, "UTF-8");
}
public static void main(String[] args) {
Map<String, String> params = new HashMap<>();
params.put("firstName", "Mickey");
params.put("lastName", "Mouse");
//自定义boundary,有两个要求:使用不会出现在发送到服务器的HTTP数据中的值;并在请求消息中的分割位置都使用相同的值
String boundary = "abcdefg";
String str = doPost("http://localhost:8888/testFile", params, boundary);
System.out.println(str);
}
通过debug,可以看出dataOutputStream的值如下:
三
总结
POST请求的两种编码格式:application/x-www-urlencoded
是浏览器默认的编码格式,用于键值对参数,参数之间用&
间隔;multipart/form-data
常用于文件等二进制,也可用于键值对参数,最后连接成一串字符传输(参考Java OK HTTP)。除了这两个编码格式,还有application/json
也经常使用。
接口代码
@RequestMapping("/task")
public class TaskController {
@RequestMapping("/")
public String index(String firstName, String lastName) {
return firstName + lastName;
}
@RequestMapping("/testFile")
public String testFile(String firstName, String lastName, MultipartFile file) {
String result = firstName + lastName;
if (file != null) {
result += (file.getOriginalFilename() != null) ? file.getOriginalFilename() : "";
}
return result;
}
}
参考网址
HTML <form> 标签的 enctype 属性:http://www.w3school.com.cn/tags/att_form_enctype.asp
HttpUrlConnection 基础使用:https://www.cnblogs.com/libertycode/p/5979260.html
SpringMvc接收multipart/form-data 传输的数据 及 PostMan各类数据类型的区别:https://www.cnblogs.com/ifindu-san/p/8251370.html#undefined
What is the boundary in multipart/form-data?:https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data
转载 https://segmentfault.com/a/1190000018774494
前言
做过前后端联调的小伙伴,可能有时会遇到一些问题。例如,我明明传递数据给后端了,后端为什么说没收到呢?这时候可能就会就会有小伙伴陷入迷茫,本文从chrome-dev-tools(F12调试器)中看到的FormData
与RequestBody
,给小伙伴们提供一种可能的思路。也给小伙伴们提供一些问题的探究方法。
简介
什么是FormData?什么是RequestPayload?不解释,直接上图:
区别?
因为这里触及了我的知识盲区,所以有了本文。这个答案是我在stackoverflow
上得到的。首先还是贴问题,贴答案。
What's the difference between “Request Payload” vs “Form Data” as seen in Chrome dev tools Network tab。
中文翻译:chrome开发者工具中的Request Payload与Form Data有什么区别?
高票回答:
The Request Payload - or to be more precise: payload body of a HTTP Request - is the data normally send by a POST or PUT Request. It's the part after the headers and the CRLF of a HTTP Request.
A request with
Content-Type: application/json
may look like this:POST /some-path HTTP/1.1 Content-Type: application/json { "foo" : "bar", "name" : "John" }
If you submit this per AJAX the browser simply shows you what it is submitting as payload body. That’s all it can do because it has no idea where the data is coming from.
If you submit a HTML-Form with method="POST" and Content-Type: application/x-www-form-urlencoded or Content-Type: multipart/form-data your request may look like this:
POST /some-path HTTP/1.1 Content-Type: application/x-www-form-urlencoded foo=bar&name=John
In this case the form-data is the request payload. Here the Browser knows more: it knows that bar is the value of the input-field foo of the submitted form. And that’s what it is showing to you.
So, they differ in the Content-Type but not in the way data is submitted. In both cases the data is in the message-body. And Chrome distinguishes how the data is presented to you in the Developer Tools.
翻译过来是说:Request Payload
更准确的说是http request的payload body
。一般用在数据通过POST
请求或者PUT
请求。它是HTTP
请求中空行的后面那部分。(PS:这里涉及一个http常被问到的问题,http请求由哪几部分组成,一般是请求行,请求头,空行,请求体。payload body应该是对应请求体。)
一个请求伴随着header设置为Content-Type: application/json
时候,看起来可能像这样:
POST /some-path HTTP/1.1
Content-Type: application/json
{ "foo" : "bar", "name" : "John" }
如果你正常请求一个ajax。浏览器会简单的将你提交的内容作为payload
展示出来,这就是它所能做的,因为它不知道数据来自哪里。
如果你提交了一个html表单并且配置上了method="post"
,并且设置了Content-Type: application/x-www-form-urlencoded
或者Content-Type: multipart/form-data
。那么你的请求可能长这个样:
POST /some-path HTTP/1.1
Content-Type: application/x-www-form-urlencoded
foo=bar&name=John
这里的form-data
就是request payload
。在这里,浏览器知道更多:它知道bar是提交表单的输入字段foo的值。这就是它向你展示的。
所以区别就是,他们只是因为Content-Type
设置的不同,并不是数据提交方式的不同,这两种提交都会将数据放在message-body
中。但是chrome浏览器的开发者工具会根据这个ContentType区分显示方式。
细节
好了,到这里我们知道了,其实都是放到了payload中。那么具体有什么区别呢?为什么有时候后端拿不到值呢?这就不得不说对payload的解析方式了。我们以几个chrome下的测试案例,来理一理这个逻辑。
传统的Form表单提交
场景构造
<form action="/" method="POST">
<input name="name" type="text">
<input name="password" type="text">
<button>提交</button>
</form>
如果我这里点击提交按钮,就会触发浏览器的提交功能,那结果是什么样呢?
注意点
可以看到Content-Type
为application/x-www-form-urlencoded
。
值得形式是以key1=value1&key2=value2
的形式提交的。
传统的ajax提交
场景构造
function submit2() {
var xhr = new XMLHttpRequest();
xhr.timeout = 3000;
var obj = {a: 1, b: 2};
xhr.open('POST', '/');
xhr.send(obj);
}
首先我们构造一个简单的函数,然后触发它。通过chrome反馈来看:
注意点
1.默认的Content-Type
为text/plain
。
2.Request Payload会对非字符串做字符串转换。
3.通过xhr.send(JSON.stringify(obj));
可修正要发的内容
axios方式提交
场景构造
由于axios已经是vue、react的准标配请求方式了,所以这里探究一下它。
首先我门看axios的文档,当post提交时候可以传递什么类型参数:
注意这个类型,我们分别构造两个场景。对应它。
function submit3() {
var sence1 = 'name=123&val=456';
var sence2 = {name: 123, val: 456};
axios.post('/', sence1)
}
分别传递字符串与对象,提交post请求,然后观察结果:
场景1——传递字符串时候的结果:
场景2——传递对象的结果:
注意点
1.当我们传递字符串的时候,Content-Type
自动转为xxx-form-xxx
的形式。当为对象的时候,自动转化为xxx/json
。
2.字符串的时候以key1=val1&key2=val2
的形式体现,对象以JSON字符串形式体现。
总结
探索了这么多种情况之后,那么我们回顾一下:
Content-Type的差异
1.传统的ajax请求时候,Content-Type
默认为"文本"类型。
2.传统的form提交的时候,Content-Type
默认为"Form"类型。
3.axios传递字符串的时候,Content-Type
默认为"Form"类型。
4.axios传递对象的时候,Content-Type
默认为"JSON"类型
Content-Type的值,Form与非Form时,payload的区别。
1.都只支持字符串类型(以上探究的几种情况)
2.Form需要传递的格式为key1=value1&key2=value2
,类似GET
请求的QueryString
格式
3.非Form一般为JSON.stringify(formDataObject)
形式
后端取不到值?
无论何种形式传递,后端解析表单信息的时候,会考虑Content-Type
。如果是JSON字符串的话,后端解析payload的内容时候,肯定要去解析JSON啦。如果是key1=value1&key2=value2
的形式,则需要去分割字符串。
当然这些事情一般后端使用的框架会去处理,但是框架给后端提供取值接口有可能是不同的,所以前端的小伙伴在处理请求问题时,一定要跟后端小伙伴商量好,是用JSON
还是FormData
哈。