http协议大家都知道是规定了以ASCII码传输,建立在tcp、ip协议之上的应用层规范,规范内容把http请求分为3个部分:状态行,请求头,请求体。所有的方法、实现都是围绕如何运用和组织这三部分来完成的。换句话来说就是万变不离其中,只要我们了解了http请求的组成部分后,自然就可以应变任何实际工作中的需求和问题了。
关于状态行,请求头,请求体等三部分的具体内容,如图所示
服务器解析http请求时候按 httphead的 Content-Type解析。
这里主要分析multipart/form-data请求具体是怎么一回事。
既然http协议本身的原始方法不支持multipart/form-data请求,那这个请求自然就是由这些原始的方法演变而来的,具体如何演变且看下文:
必须包含Content-Type信息,且其值也必须规定为multipart/form-data,同时还需要规定一个内容分割符用于分割请求体中不同参数的内容(普通post请求的参数分割符默认为&,参数与参数值的分隔符为=)。具体的头信息格式如下:
Content-Type: multipart/form-data; boundary=${bound}
其中${bound} 是一个占位符,代表我们规定的具体分割符;可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。如:--------------------56423498738365
样例:
Content-Type: multipart/form-data; boundary=--------------------56423498738365
multipart/form-data请求体:
它也是一个字符串,不过和普通post请求体不同的是它的构造方式。普通post请求体是简单的键值对连接,格式如下:
k1=v1&k2=v2&k3=v3
而multipart/form-data则是添加了分隔符、参数描述信息等内容的构造体。具体格式如下:
${bound}
Content-Disposition: form-data; name="Filename"
HTTP.pdf
${bound}
Content-Disposition: form-data; name="file000"; filename="HTTP协议详解.pdf"
Content-Type: application/octet-stream
%PDF-1.5
file content
%%EOF
${bound}
Content-Disposition: form-data; name="Upload"
Submit Query
${bound}--
其中${bound}就是之前请求头信息中的分割符,如果头信息中规定为123,那么这里也要为123(其作用等同于普通post请求中的&符号的作用,即分割参数)。很明显如果按照之前请求头的样例来,则这里的${bound}值应该替换为--------------------56423498738365,注意最后一个${bound}后需要固定添加两个横杆--
另外可以很容易的看出,这个请求体被分隔符划分为3个部分,而每个部分其实就是一个参数的键值描述(其作用等同于普通post请求的k1=v1的部分),但对参数信息的描述可以比普通post请求更加丰富,这就是为什么multipart/form-data能发送文件的原因。
每一个部分都是以分隔符开始的,接着是该部分内容的描述信息,然后是一个回车,最后是描述信息的具体内容。如果传送的内容是一个文件的话,那么还会包含文件名信息,以及文件内容的类型;上面的第二个小部分其实是一个文件体的结构,最后的分割符后会以--结尾,表示请求体结束。
综上,可以知道要发送一个multipart/form-data的请求,其实任何支持post请求的工具或语言都可以支持,只是自己要稍微包装一下便可。
看下用工具抓包的的数据
下面的方法是C#的一个封装
/// <summary>
/// 发起httpPost 请求,可以上传文件
/// </summary>
/// <param name="url">请求的地址</param>
/// <param name="files">文件</param>
/// <param name="input">表单数据</param>
/// <param name="endoding">编码</param>
/// <returns></returns>
public static string PostResponse(string url, UpLoadFile[] files, Dictionary<string, string> input, Encoding endoding)
{
string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
//request.Credentials = CredentialCache.DefaultCredentials;
request.Expect = "";
MemoryStream stream = new MemoryStream();
byte[] line = Encoding.ASCII.GetBytes("--" + boundary + "\r\n");
byte[] enterER = Encoding.ASCII.GetBytes("\r\n");
////提交文件
if (files != null)
{
string fformat = "Content-Disposition:form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type:{2}\r\n\r\n";
foreach (UpLoadFile file in files)
{
stream.Write(line, 0, line.Length); //项目分隔符
string s = string.Format(fformat, file.Name, file.FileName, file.Content_Type);
byte[] data = Encoding.UTF8.GetBytes(s);
stream.Write(data, 0, data.Length);
stream.Write(file.Data, 0, file.Data.Length);
stream.Write(enterER, 0, enterER.Length); //添加\r\n
}
}
//提交文本字段
if (input != null)
{
string format = "--" + boundary + "\r\nContent-Disposition:form-data;name=\"{0}\"\r\n\r\n{1}\r\n"; //自带项目分隔符
foreach (string key in input.Keys)
{
string s = string.Format(format, key, input[key]);
byte[] data = Encoding.UTF8.GetBytes(s);
stream.Write(data, 0, data.Length);
}
}
byte[] foot_data = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n"); //项目最后的分隔符字符串需要带上--
stream.Write(foot_data, 0, foot_data.Length);
request.ContentLength = stream.Length;
Stream requestStream = request.GetRequestStream(); //写入请求数据
stream.Position = 0L;
stream.CopyTo(requestStream); //
stream.Close();
requestStream.Close();
try
{
HttpWebResponse response;
try
{
response = (HttpWebResponse)request.GetResponse();
try
{
using (var responseStream = response.GetResponseStream())
using (var mstream = new MemoryStream())
{
responseStream.CopyTo(mstream);
string message = endoding.GetString(mstream.ToArray());
return message;
}
}
catch (Exception ex)
{
throw ex;
}
}
catch (WebException ex)
{
//response = (HttpWebResponse)ex.Response;
//if (response.StatusCode == HttpStatusCode.BadRequest)
//{
// using (Stream data = response.GetResponseStream())
// {
// using (StreamReader reader = new StreamReader(data))
// {
// string text = reader.ReadToEnd();
// Console.WriteLine(text);
// }
// }
//}
throw ex;
}
}
catch (Exception ex)
{
throw ex;
}
}
本文链接:https://blog.nnwk.net/article/130
有问题请留言。版权所有,转载请在显眼位置处保留文章出处,并留下原文连接
Leave your question and I'll get back to you as soon as I see it. All rights reserved. Please keep the source and links
友情链接:
子卿全栈
全部评论