在常见业务开发中,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) ;
HttpURLConnection urlConnection = ( HttpURLConnection) url. openConnection ( ) ;
urlConnection. setRequestMethod ( "POST" ) ;
urlConnection. setConnectTimeout ( 30000 ) ;
urlConnection. setReadTimeout ( 30000 ) ;
urlConnection. setDoOutput ( true ) ;
urlConnection. setRequestProperty ( "content-type" , "application/x-www-form-urlencoded" ) ;
OutputStream outputStream = urlConnection. getOutputStream ( ) ;
outputStream. write ( content. getBytes ( ) ) ;
outputStream. flush ( ) ;
outputStream. 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 ( 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 ) ;
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) ;
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" ) ;
String boundary = "abcdefg" ;
String str = doPost ( "http://localhost:8888/testFile" , params , boundary) ;
System. out . println ( str) ;
}
通过debug,可以看出dataOutputStream
的值如下:
三、使用Postman
测试接口时 1、POST请求 -> Body -> x-www-form-urlencoded
当切换为x-www-form-urlencoded
时,Headers
会自动添加Content-Type:application/x-www-form-urlencoded
当请求Send后,此时点Code
,可以查看到和Chrome开发工具中(Request Headers
处的Content-Type
和Form Data
)一样的数据
2、POST请求 -> Body -> form-data
相当于html表单请求,value可为Text或文件。
可以不用手动指定编码格式,也可以指定编码为multipart/form-data
划线处的分割线应该是被省略了。 可以更改左上角的类型,来查看相应的Headers
代码,常见的是下面三种:
Java OK HTTP:
JavaScript Jquery AJAX: JavaScript XHR:
总结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;
}
}
四种常见的 POST 提交数据方式HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT
这几种。其中 POST
一般用来向服务端提交数据,本文主要讨论 POST 提交数据的几种方式。
我们知道,HTTP 协议是以 ASCII
码传输,建立在 TCP/IP
协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行请求行、请求头、消息主体。类似于下面这样:
< method> < request-URL> < version>
< headers>
< entity-body>
协议规定 POST 提交的数据必须放在消息主体(entity-body
)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。
但是,数据发送出去,还要服务端解析成功才有意义。一般服务端语言如 php、python 等,以及它们的 framework,都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的 Content-Type
字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到 POST
提交数据方案,包含了 Content-Type
和消息主体编码方式两部分。下面就正式开始介绍它们。
application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 <form>
表单,如果不设置 enctype
属性,那么最终就会以 application/x-www-form-urlencoded
方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了):
POST http:
Content- Type: application/ x- www- form- urlencoded; charset= utf- 8
title= test& sub% 5 B% 5 D= 1 & sub% 5 B% 5 D= 2 & sub% 5 B% 5 D= 3
首先,Content-Type
被指定为 application/x-www-form-urlencoded;
其次,提交的数据按照 key1=val1&key2=val2
的方式进行编码,key
和 val
都进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。例如 PHP 中,$_POST['title']
可以获取到 title
的值,$_POST['sub']
可以得到 sub
数组。
很多时候,我们用 Ajax 提交数据时,也是使用这种方式。例如 JQuery 和 QWrap 的 Ajax,Content-Type
默认值都是「application/x-www-form-urlencoded;charset=utf-8」
。
multipart/form-data
这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 <form>
表单的 enctype
等于 multipart/form-data
。直接来看一个请求示例:
POST http:
Content- Type: multipart/ form- data; boundary= -- -- WebKitFormBoundaryrGKCBY7qhFd3TrwA
-- -- -- WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content- Disposition: form- data; name= "text"
title
-- -- -- WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content- Disposition: form- data; name= "file" ; filename= "chrome.png"
Content- Type: image/ png
PNG . . . content of chrome. png . . .
-- -- -- WebKitFormBoundaryrGKCBY7qhFd3TrwA--
这个例子稍微复杂点。首先生成了一个 boundary
用于分割不同的字段,为了避免与正文内容重复,boundary
很长很复杂。然后 Content-Type
里指明了数据是以 multipart/form-data
来编码,本次请求的 boundary
是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary
开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary--
标示结束。关于 multipart/form-data
的详细定义,请前往 rfc1867 查看。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form>
表单也只支持这两种方式(通过 <form>
元素的 enctype
属性指定,默认为 application/x-www-form-urlencoded
。其实 enctype
还支持 text/plain
,不过用得非常少)。
随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax
进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。
application/json
application/json
这个 Content-Type
作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify
,服务端语言也都有处理 JSON
的函数,使用 JSON
不会遇上什么麻烦。
JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。记得我几年前做一个项目时,需要提交的数据层次非常深,我就是把数据 JSON
序列化之后来提交的。不过当时我是把 JSON 字符串作为 val
,仍然放在键值对里,以 x-www-form-urlencoded
方式提交。
Google 的 AngularJS 中的 Ajax
功能,默认就是提交 JSON
字符串。例如下面这段代码:
var data = { 'title' : 'test' , 'sub' : [ 1 , 2 , 3 ] } ;
$http. post ( url, data) . success ( function ( result ) {
...
} ) ;
最终发送的请求是:
POST http: / / www. example. com HTTP / 1.1
Content- Type: application/ json; charset= utf- 8
{ "title" : "test" , "sub" : [ 1 , 2 , 3 ] }
这种方案,可以方便的提交复杂的结构化数据,特别适合 RESTful 的接口。各大抓包工具如 Chrome 自带的开发者工具、Firebug、Fiddler,都会以树形结构展示 JSON 数据,非常友好。但也有些服务端语言还没有支持这种方式,例如 php 就无法通过 $_POST
对象从上面的请求中获得内容。这时候,需要自己动手处理下:在请求头中 Content-Type
为 application/json
时,从 php://input 里获得原始输入流,再 json_decode
成对象。一些 php 框架已经开始这么做了。
当然 AngularJS 也可以配置为使用 x-www-form-urlencoded
方式提交数据。如有需要,可以参考这篇文章。
text/xml我的博客之前提到过 XML-RPC(XML Remote Procedure Call)
。它是一种使用 HTTP 作为传输协议,XML
作为编码方式的远程调用规范。典型的 XML-RPC 请求是这样的:
POST http://www.example.com HTTP/1.1
Content-Type: text/xml
<?xml version="1.0"?>
< methodCall>
< methodName> examples.getStateName</ methodName>
< params>
< param>
< value> < i4> 41</ i4> </ value>
</ param>
</ params>
</ methodCall>
XML-RPC
协议简单、功能够用,各种语言的实现都有。它的使用也很广泛,如 WordPress 的 XML-RPC Api,搜索引擎的 ping 服务等等。JavaScript 中,也有现成的库支持以这种方式进行数据交互,能很好的支持已有的 XML-RPC
服务。不过,我个人觉得 XML
结构还是过于臃肿,一般场景用 JSON 会更灵活方便。
来自 https://blog.csdn.net/WuLex/article/details/118771912
https://blog.csdn.net/WuLex/article/details/118771912
Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data
在用POST提交表单 时,常常遇到提交时content-tpyes是application/x-www-form-urlencoded和multipart/form-data的情况。 其本质的区别在于消息体的编码 application/x-www-form-urlencoded采用 “名称=值”=的组合以&项链,其中,名称和值经过了百分号编码 而multipart/form-data不会对参数编码,使用的boundary(分割线),相当于&
前端代码<form name="input" action="#" method="POST">
Username: <input type="text" name="user">
<input type="text" name="password">
<input type="submit" value="Submit">
</form>
我们输入账号:努力的shiki 密码:123456 通过使用Filddle4抓包我们可以看见:
请求消息(Request): 可以一一对应来学习:
application/x-www-form-urlencoded通过POST方法发送的请求消息中包含消息体。与响应消息一样、消息头和消息体用空行分隔,POST方法发送的值被放在请求的消息体(请求数据)中
与POST发送值相关的消息头为Context-Lenghth和Content-Type:
Context-Lenghth content-type 关于百分号编码中文和特殊字符不能直接用于URL,如果用在URL与要经过百分号编码, 百分号编码是将字符以字节为单位转换成%xx的形式。 例如本例中 账号为努力的shiki 在消息体中为user=%26%2321162%3B%26%2321147%3B%26%2330340%3Bshiki 这里已经解码成功了
multipart/form-data那么当服务器使用multipart/form-data接收POST请求时,服务器怎么知道每个参数的开始位置和结束位置呢?
<form action="#" method="POST" enctype="multipart/form-data">
First name: <input type="text" name="name" value="努力的shiki"><br>
Last name: <input type="text" name="password" value="123456 "><br>
<input type="submit" value="提交">
</form>
我们开始抓包:
我们是可以看到
和消息体中的
------WebKitFormBoundaryHS1waI401BVPLvvJ Content-Disposition: form-data; name=“name”
努力的shiki ------WebKitFormBoundaryHS1waI401BVPLvvJ Content-Disposition: form-data; name=“password”
123456 ------WebKitFormBoundaryHS1waI401BVPLvvJ–
可以看出multipart/form-data不会对参数编码,使用的boundary(分割线),相当于&,boundary的值是----Web**AJv3。 我们来引用一张图来说明
文件上传时、要指定编码格式为multipart/form-data
来自 https://blog.csdn.net/m0_50219079/article/details/118662566