我们在提交表单的时候,form表单参数中会有一个enctype的参数。enctype指定了HTTP请求的Content-Type。
常用有两种:application/x-www-form-urlencoded和multipart/form-data。
application/x-www-form-urlencoded: 窗体数据被编码为名称/值对,并且将提交的数据进行urlencode。默认情况下,我们所有的表单提交都是通过这种默认的方式实现的。
multipart/form-data: 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。
当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串append到url后面,用?分割,加载这个新的url。
当action为post时候,浏览器把form数据封装到http body中,然后发送到server。 如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。
请求标头:
请求正文:
loginType=1&loginName=operator&pwd=%E1%8D%B0%E1%8D%B0%E1%8D%B0%E1%8D%B0%E1%8D%B0%E1%8D%B0&verifyCode=6987
首先,Content-Type 被指定为 application/x-www-form-urlencoded;其次,提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。
但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符(boundary)。
Multipart/form-data其实就是浏览器用表单上传文件的方式。最常见的情境是:在写邮件时,向邮件后添加附件,附件通常使用表单添加,也就是用multipart/form-data格式上传到服务器。
具体的步骤是怎样的呢?
首先,客户端和服务器建立连接(TCP协议)。
第二,客户端可以向服务器端发送数据。因为上传文件实质上也是向服务器端发送请求。
第三,客户端按照符合“multipart/form-data”的格式向服务器端发送数据。
服务端接收的http请求标头:
请求正文:
-----------------------------7e11a616672236 Content-Disposition: form-data; name="id" 17e832d6a79744eaae6d79237e82e3a4 -----------------------------7e11a616672236 Content-Disposition: form-data; name="templateType" 1 -----------------------------7e11a616672236 Content-Disposition: form-data; name="file"; filename="" Content-Type: application/octet-stream <二进制文件数据未显示> ---------------------------7e11a616672236 Content-Disposition: form-data; name="imagesTemplate" -----------------------------7e11a616672236 Content-Disposition: form-data; name="attachTemplate" -----------------------------7e11a616672236 Content-Disposition: form-data; name="ajax" 1 -----------------------------7e11a616672236--
这行指出这个请求是“multipart/form-data”格式的,且“boundary”是 “---------------------------7e11a616672236”这个字符串。
不难想象,“boundary”是用来隔开表单中不同部分数据的。例子中的表单就有 6 部分数据,用“boundary”隔开。“boundary”一般由系统随机产生,但也可以简单的用“-------------”来代替。
实际上,每部分数据的开头都是由"--" + boundary开始,而不是由 boundary 开始。仔细看才能发现下面的开头这段字符串实际上要比 boundary 多了个 “--”
紧接着 boundary 的是该部分数据的描述。
在请求的最后,则是 "--" + boundary + "--" 表明表单的结束。
需要注意的是,在html协议中,用 “/r/n” 换行,而不是 “/n”。
附C#组装代码:
var memStream = new MemoryStream(); //https支持 ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(httpUrl); // 边界符 var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); // 边界符 var beginBoundary = Encoding.ASCII.GetBytes("--" + boundary + "\r\n"); // 最后的结束符 var endBoundary = Encoding.ASCII.GetBytes("--" + boundary + "--\r\n"); memStream.Write(beginBoundary, 0, beginBoundary.Length); // 设置属性 request.Method = "POST"; request.Timeout = 50000000; request.ReadWriteTimeout = 30000000; request.ContentType = "multipart/form-data; boundary=" + boundary; var stringKeyHeader = "Content-Disposition: form-data; name=\"{0}\"" + "\r\n\r\n{1}\r\n"; var header = string.Format(stringKeyHeader, "REQMESSAGE", bodyJson); var headerbytes = Encoding.UTF8.GetBytes(header); memStream.Write(headerbytes, 0, headerbytes.Length); memStream.Write(beginBoundary, 0, beginBoundary.Length); header = string.Format(stringKeyHeader, "AUTHMESSAGE", authJson); headerbytes = Encoding.UTF8.GetBytes(header); memStream.Write(headerbytes, 0, headerbytes.Length); memStream.Write(endBoundary, 0, endBoundary.Length); request.ContentLength = memStream.Length; Stream requestStream = request.GetRequestStream(); memStream.Position = 0; var tempBuffer = new byte[memStream.Length]; memStream.Read(tempBuffer, 0, tempBuffer.Length); memStream.Close(); requestStream.Write(tempBuffer, 0, tempBuffer.Length); requestStream.Close(); /*string s = System.Text.Encoding.UTF8.GetString(tempBuffer, 0, tempBuffer.Length); Console.WriteLine(s);*/ HttpWebResponse response = (HttpWebResponse)request.GetResponse(); StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));//("gb2312")); resultInfo = sr.ReadToEnd();
实现的是传递两个json串