The story behind
Recently we received access to a third-party Java web service offering us some awesome features we wished to implement into our .NET client application. We were provided with a WSDL web service specification and some schema definition files. We simply generated C# proxy classes and started to play with all those new awesome features. After a while we noticed that some of the provided methods are missing in our proxy class. We were sniffing around and found following comment in the proxy class:
// CODEGEN: The operation binding ‘AwesomeMethod’ from namespace ‘http://awesome.namespace/wsdl’ was ignored. Missing soap:body input binding.
<wsdl:input name="AwesomeMethodInput">
<mime:multipartRelated>
<mime:part>
<soap:body use="literal" part="awesomeMethod"/>
</mime:part>
<mime:part>
<mime:content part="uploadFile" type="application/octet-stream"/>
</mime:part>
</mime:multipartRelated>
</wsdl:input>
SOAP web client
var request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "text/xml; charset=utf-8";
request.Method = "POST";
request.Timeout = timeout;
request.Credentials = new NetworkCredential("username", "password");
using (var stream = request.GetRequestStream())
{
using (var writer = new StreamWriter(stream))
{
writer.Write(postData);
}
}
request.Headers.Add("SoapAction", @"""awesome.namespace#AwesomeMethod""");
As you can see, the action name consists of method namespace with method name, both delimited by hash. The specification must be wraped in quotation marks otherwise the request will not be performed.
2) Construct a SOAP request according to specification and append it to POST body according to your WSDL specification:
var requestString = @"<? xml version=""1.0"" encoding=""utf-8"" ?>
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope"">
<soap:Body><message-content/>
</soap:Body>
</soap:Envelope>";
How to attach file
If you want to upload a file to a remote SOAP web service, you have to send its content as a part of multipart MIME message. There are many multipart content types which can be used; probably the most common is multipart/form-data used for sending data from web-forms. In our case, the request had to be sent as multipart/related content according to the specification, therefore in the following we will focus on construction of such requests.
First of all, you have to specify appropriate content type and boundary in your request header. The boundary is essential part of every multipart content type, it is any string which delimits single parts of the message and denotes end of the message. The header should be specified as following:
request.ContentType = @"multipart/related; boundary=some_boundary; charset=utf-8; type=text/xml; start=""<first-part>""";
At first you can see specification of our content type followed by definition of boundary (quotation marks must not be used, no white spaces are allowed). Attribute start defines the first part of the message itself through its Content-ID; for multipart/related content type, this attribute must be specified and wrapped in quotation marks. So, if you have the header specified, the message construction with uploaded file is quite simple. You just have to compose following request message and set it as your request body:
--some_boundary Content-ID: <first-part> Content-Type: text/xml; charset=utf-8 <?xml version='1.0'?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <message-content/> </soap:Envelope> --some_boundary Content-ID: <second-part> Content-Type: application/octet-stream Content-Transfer-Encoding: binary ...@a!+s1dw32w16qw...your-binary-file…s+61... --some_boundary--
The specification is quite strict, therefore the request must be constructed exactly as you see it in the example. I mean, boundaries must be prefixed with a double-dash. The footer of whole message must be in suffixed with another double-dash, each header must be on a new line, headers and content itself must be delimited with CRLF constant and so on.
Note that Content-Transfer-Encoding header specifies how the uploaded file will be transferred – for example you can specify binary, base64, utf-8, etc.. This value might depend on specification of your web service. It also determines the way how you will treat content of uploaded file before you attach it to the request body. For example, in case of base64 transfer encoding you have to encode whole file into base64. If you set transfer encoding to binary, you should use BinaryWriter instead of StreamWriter for uploading request body data into your request stream otherwise the uploaded file might be corrupted in your request.
Hi
Thank you for this article and it helped me a lot. I could get response from a web service adapting this technique. Is there a neater way to parse the multipart response though ?
Thanks
Jyotsna
Hi Jyotsna, thank you for comment and sorry for my late response. I am glad I could help. Unfortunately, I didn’t get into situation where I had to parse multipart SOAP responses therefore I have no hands-on experience on this. I hope you have already solved it anyway!