C#记一次http协议multipart/form-data的boundary问题

2022-07-14,,,,

1.问题描述

使用post方法调用上级联网厂家接口,返回http状态码415,返回信息content type ‘application/x-www-form-urlencoded’ not supported

测试上级联网厂家接口使用的是postman工具,工具下载地址:https://www.getpostman.com/downloads/

使用application/x-www-form-urlencoded调用接口,返回http状态码415,如图:

既然服务器无法处理请求附带的媒体格式,那么改用multipart/form-data试试?

测试后发现可以调用成功,如图:

我们都知道contenttype为application/x-www-form-urlencoded的请求头、体如何构造,如:

httpwebrequest request = webrequest.create(new uri(url)) as httpwebrequest;
request.method = "post";
request.host = properties.settings.default.ip;
request.contenttype = "application/x-www-form-urlencoded; charset=utf-8";
request.useragent = "mozilla/5.0 (windows nt 6.3; wow64; trident/7.0; rv:11.0) like gecko";
request.accept = "*/*";
request.cookiecontainer = cc;
request.keepalive = true;
string postdata = string.format("action=cx&hphm={0}&hpzl=&jclb=&detlsh=&clpp=&clxh=&rlzl=&pfbz=&jcff=&evl=&staname={1}&detlineid=&syxz=&rqyi={2}&rqer={3}&rqxz=jcrq&cxjl=jiance&cllb=&clgs=&zcrq=&zzl=&clsbdh=&syr=&ccdjrq=&page={4}&rows=10", hphm, staname, rqyi, rqer, page);
byte[] postdatabyte = encoding.getencoding("utf-8").getbytes(postdata);
request.contentlength = postdatabyte.length;
using (stream stream = request.getrequeststream())
{
	stream.write(postdatabyte, 0, postdatabyte.length);
}
httpwebresponse response = request.getresponse() as httpwebresponse;
using (streamreader reader = new streamreader(response.getresponsestream(), encoding.getencoding("utf-8")))
{
	string temp = reader.readtoend();
}

但是!multipart/form-data的请求头与请求体该如何构造呢?像下面这样?

string url = "http://112.17.158.12:8180/intf/services/query";
httpwebrequest request = webrequest.create(new uri(url)) as httpwebrequest;
request.method = "post";
request.host = "112.17.158.12:8180";
request.contenttype = "multipart/form-data; ";
request.useragent = "postmanruntime/7.17.1";
request.accept = "*/*";
request.keepalive = true;
string postdata = @"jkuser=33088102&jkpasswd=33088102&jsondata={""jkid"":""r10"",""requesttime"":""20190919110603"",""body"":[{""inspstationcode"":""33088102""}]}";
byte[] postdatabyte = encoding.getencoding("utf-8").getbytes(postdata);
request.contentlength = postdatabyte.length;
using (stream stream = request.getrequeststream())
{
	stream.write(postdatabyte, 0, postdatabyte.length);
}
httpwebresponse response = request.getresponse() as httpwebresponse;
using (streamreader reader = new streamreader(response.getresponsestream(), encoding.getencoding("utf-8")))
{
	string temp = reader.readtoend();
}

显然不对,捕获到了"远程服务器返回错误:(500)内部服务器错误。"异常,那么我们该怎么办呢?

2.解决思路

既然postman工具使用contenttype为multipart/form-data类型post数据可以成功,那么我们写的c#程序应该也可以呀!那怎么办呢?。。。。没错!!就是抓包!

首先想到的是,用fiddler抓postman的数据包,然后c#程序构造同样的数据包即可。

观察数据包headers选项卡,如图:

发现content-type: multipart/form-data;后面跟了一个boundary=----------------------------183584948778966847113836

并且textview选项卡如下:

webforms选项卡如下:

所以,可以按照headers和textview选项卡内容构造post请求,就可以解决我们的问题了!

3.解决步骤

将contenttype加上boundary=boundary-------------------------xxxxxxxxxxxxxx

并且构造参数,如下:

string url = "http://112.17.158.12:8180/intf/services/query";
string boundary = "--------------------------" + datetime.now.ticks.tostring("x");
string boundary2 = "--" + boundary;
httpwebrequest request = webrequest.create(new uri(url)) as httpwebrequest;
request.method = "post";
request.host = "112.17.158.12:8180";
request.contenttype = "multipart/form-data; boundary=" + boundary;
request.useragent = "postmanruntime/7.17.1";
request.accept = "*/*";
request.keepalive = true;
stringbuilder sb = new stringbuilder();
sb.append(boundary2 + "\r\n");
sb.append(@"content-disposition: form-data; name=""jkuser""" + "\r\n\r\n");
sb.append("33088102" + "\r\n");
sb.append(boundary2 + "\r\n");
sb.append(@"content-disposition: form-data; name=""jkpasswd""" + "\r\n\r\n");
sb.append("33088102" + "\r\n");
sb.append(boundary2 + "\r\n");
sb.append(@"content-disposition: form-data; name=""jsondata""" + "\r\n\r\n");
sb.append(@"{""jkid"":""r10"",""requesttime"":""20190919110603"",""body"":[{""inspstationcode"":""33088102""}]}" + "\r\n");
sb.append(boundary2 + "--" + "\r\n");
string postdata = sb.tostring();
byte[] postdatabyte = encoding.getencoding("utf-8").getbytes(postdata);
request.contentlength = postdatabyte.length;
using (stream stream = request.getrequeststream())
{
    stream.write(postdatabyte, 0, postdatabyte.length);
}
httpwebresponse response = request.getresponse() as httpwebresponse;
using (streamreader reader = new streamreader(response.getresponsestream(), encoding.getencoding("utf-8")))
{
    string temp = reader.readtoend();
}

值得注意的是,“boundary-------------------------xxxxxxxxxxxxxx”

这么长一串东西,只是作为分隔符出现的,不必太在意它是什么东西,我将它理解为分割文本参数的这么一个东西,并且通过仔细观察发现可以发现header中contenttype的横线数量比参数中横线数量少两个且必须少两个?

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。 

《C#记一次http协议multipart/form-data的boundary问题.doc》

下载本文的Word格式文档,以方便收藏与打印。