如何用C#从SOAP响应提取MTOM/XOP附件并保存为PDF
处理MTOM/XOP格式SOAP响应中的PDF附件(C#实现)
问题背景
需要从包含MTOM(XOP)附件的SOAP响应中提取PDF文档,反序列化XOP得到二进制数据并保存为PDF文件。当前使用StreamReader读取响应的方式无法正确处理二进制附件,但SoapUI开启“Expand MTOM Attachment”可正常获取PDF,寻求C#可行实现方案。
收到的SOAP响应
--uuid:277c111e-1c6c-4da9-9e54-95ec1073550e Content-Type: application/xop+xml; charset=UTF-8; type="text/xml" Content-Transfer-Encoding: binary Content-ID: <root.message@cxf.apache.org> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://www.endpoint.com/api/"> <soapenv:Header> <ResponseHeader state="OK" timelasted="116"/> </soapenv:Header> <soapenv:Body> <GetDocumentResponse> <idDocument>1</idDocument> <content> <mime>application/pdf</mime> <name>document.pdf</name> <data fileSize="15756"> <inc:Include xmlns:inc="http://www.w3.org/2004/08/xop/include" href="cid:1234"/> </data> </content> </GetDocumentResponse> </soapenv:Body> </soapenv:Envelope> --uuid:277c111e-1c6c-4da9-9e54-95ec1073550e Content-Type: application/pdf Content-Transfer-Encoding: binary Content-ID: <1234> ۞t�:�=��M��=����������M�� ��M��N��n�� ��-��^��^���8ﮅ�n����Nt�^�랂�݅�~�략�m��^��N��~��Nt���N�M��^t�n����~��m��^��N��~��Nt�:�N��^��M��N9獴�m��M��M��݅�~�략�^�㎵��8� ��M��}��M��]�ߝ�����M��M�۽��M��݅�>��8�^�라���m��m��]��}��]��]�ߝ����M��M�۽��^x�z�^<��8���ߍ�ߝ�߭��M��}��^x�z�^7�>t�~�9�ͽ�^x�z�^w�5�nx�5�9�ͽ�M��=
现有C#请求代码片段
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(endpoint); req.Headers.Add("SOAP:Action"); req.ContentType = "text/xml;charset=utf-8"; req.Method = "POST"; req.Accept = "application/pdf"; req.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes("username:password")); req.Credentials = new NetworkCredential(username, password); var soapRequest = @"XML_Request"; using (StreamWriter writer = new StreamWriter(req.GetRequestStream())) { writer.Write(soapRequest); } using (var httpResponse = (HttpWebResponse)req.GetResponse()) { var streamReader = new StreamReader(httpResponse.GetResponseStream()); result = streamReader.ReadToEnd(); }
解决方案
MTOM响应是多部分MIME消息,不能直接用StreamReader读取文本,需要解析多部分内容,匹配cid找到对应的PDF附件流。
关键步骤
- 调整请求头:将
Accept设置为multipart/related; type="application/xop+xml",明确告知服务端返回MTOM格式响应。 - 解析多部分响应:使用
MultipartMemoryStreamProvider自动分割多部分内容,将每个部分封装为可操作的对象。 - 提取并保存PDF:遍历所有响应部分,匹配SOAP中
inc:Include指向的cid值,将对应部分的二进制流保存为PDF文件。
完整示例代码
using System; using System.IO; using System.Net; using System.Net.Mime; using System.Text; class Program { static void Main(string[] args) { string endpoint = "你的API端点地址"; string username = "用户名"; string password = "密码"; var soapRequest = @"你的SOAP请求XML内容"; // 创建请求并配置参数 HttpWebRequest req = (HttpWebRequest)WebRequest.Create(endpoint); req.Headers.Add("SOAPAction", ""); // 替换为实际的SOAP Action值 req.ContentType = "text/xml;charset=utf-8"; req.Method = "POST"; // 设置Accept头支持MTOM格式 req.Accept = "multipart/related; type=\"application/xop+xml\""; req.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes($"{username}:{password}")); req.Credentials = new NetworkCredential(username, password); // 发送SOAP请求 using (StreamWriter writer = new StreamWriter(req.GetRequestStream())) { writer.Write(soapRequest); } // 处理MTOM响应 using (var httpResponse = (HttpWebResponse)req.GetResponse()) { if (httpResponse.ContentType.StartsWith("multipart/related")) { var provider = new MultipartMemoryStreamProvider(); using (var responseStream = httpResponse.GetResponseStream()) { provider.ReadStreamAsync(responseStream, httpResponse.ContentType, null).Wait(); } // 遍历所有响应部分,匹配目标cid foreach (var part in provider.Contents) { var contentId = part.Headers.ContentId; // 匹配SOAP中cid:1234对应的Content-ID(格式为<1234>) if (contentId == "<1234>") { // 保存二进制内容为PDF文件 using (var fileStream = new FileStream("document.pdf", FileMode.Create)) { part.CopyToAsync(fileStream).Wait(); } Console.WriteLine("PDF文件已成功保存"); break; } } } else { Console.WriteLine("响应并非MTOM格式"); } } } }
代码说明
- 请求头优化:
Accept设置为MTOM专属格式,确保服务端返回正确的多部分响应。 - MultipartMemoryStreamProvider:自动处理多部分MIME的边界分割,无需手动解析原始流。
- cid匹配逻辑:SOAP中的
inc:Include指向cid:1234,对应附件的Content-ID为<1234>,需精确匹配该值才能定位到目标PDF流。
内容的提问来源于stack exchange,提问作者JohnB
相关产品推荐
相关产品推荐

