如何在.NET Framework 4.8/.NET Core中用HttpClient发送带PDF附件的SOAP API请求?
问题分析
你遇到的错误是因为服务端的documentData字段被定义为base64Binary类型,但你的请求中直接传入了cid:NETRoadmap.pdf字符串,不符合MTOM(Message Transmission Optimization Mechanism)的规范。SOAP UI启用MTOM后正常,说明服务端支持MTOM传输,你需要按照MTOM的格式构建请求,而非普通的multipart附件。
解决方案
方案1:使用WCF客户端(推荐,原生支持MTOM)
.NET Framework中WCF对MTOM有原生支持,相比手动构建请求更简单可靠:
添加服务引用
在项目右键 → 添加 → 服务引用,输入SOAP API的WSDL地址,生成客户端代理类。配置MTOM绑定
修改App.config或Web.config的绑定配置,启用MTOM:<system.serviceModel> <bindings> <basicHttpBinding> <binding name="YourServiceBinding" messageEncoding="Mtom"> <!-- 根据服务要求配置其他属性,比如安全、超时等 --> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="soapapiurl" binding="basicHttpBinding" bindingConfiguration="YourServiceBinding" contract="YourServiceNamespace.YourServiceContract" /> </client> </system.serviceModel>调用API上传文件
使用生成的客户端类直接传入文件字节数组:public static void Main(string[] args) { string filePath = @"D:\C#Projects\CarFinanceFiles\NETRoadmap.pdf"; byte[] fileBytes = File.ReadAllBytes(filePath); using (var client = new YourServiceClient()) { var request = new SendDocumentRequest { documentUploader = new DocumentUploader { fileType = "PAY", documentName = "NETRoadmap", documentData = fileBytes } }; try { var response = client.SendDocument(request); Console.WriteLine("上传成功"); // 处理响应 } catch (Exception ex) { Console.WriteLine($"错误:{ex.Message}"); } } }
方案2:手动构建MTOM格式的HttpClient请求
如果必须使用HttpClient手动构建请求,需要按照MTOM规范调整SOAP信封和请求头:
修改后的代码如下:
public class Program { public static async Task Main(string[] args) { string serviceUrl = "soapapiurl"; string boundary = $"----=_Part_{Guid.NewGuid():N}"; string soapPartCid = "<soapPart>"; string attachmentCid = "<NETRoadmap.pdf>"; // 读取文件 string filePath = @"D:\C#Projects\CarFinanceFiles\NETRoadmap.pdf"; byte[] fileBytes = File.ReadAllBytes(filePath); // 构建符合MTOM规范的SOAP信封,使用xop:Include引用附件 string soapEnvelope = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:vo=""http://vo.service.ws.proposal.wcg.com"" xmlns:xop=""http://www.w3.org/2004/08/xop/include""> <soapenv:Header/> <soapenv:Body> <vo:SendDocumentRequest> <vo:documentUploader> <vo:fileType>PAY</vo:fileType> <vo:documentName>NETRoadmap</vo:documentName> <vo:documentData> <xop:Include href=""cid:NETRoadmap.pdf"" /> </vo:documentData> </vo:documentUploader> </vo:SendDocumentRequest> </soapenv:Body> </soapenv:Envelope>"; // 创建multipart/related内容,符合MTOM要求 var multipartContent = new MultipartContent("related", boundary); multipartContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("type", "\"application/xop+xml\"")); multipartContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("start", soapPartCid)); multipartContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("start-info", "\"text/xml\"")); // 添加SOAP部分 var soapContent = new StringContent(soapEnvelope, Encoding.UTF8, "application/xop+xml"); soapContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", "utf-8")); soapContent.Headers.Add("Content-ID", soapPartCid); multipartContent.Add(soapContent); // 添加附件部分 var attachmentContent = new ByteArrayContent(fileBytes); attachmentContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); attachmentContent.Headers.Add("Content-ID", attachmentCid); // 不需要Content-Disposition,MTOM依赖Content-ID关联 multipartContent.Add(attachmentContent); using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Add("SOAPAction", "SendDocument"); HttpResponseMessage response = await client.PostAsync(serviceUrl, multipartContent); if (response.IsSuccessStatusCode) { string responseContent = await response.Content.ReadAsStringAsync(); Console.WriteLine("响应内容:"); Console.WriteLine(responseContent); } else { Console.WriteLine($"错误状态码:{response.StatusCode}"); string errorContent = await response.Content.ReadAsStringAsync(); Console.WriteLine(errorContent); } } } }
关键修改点:
- SOAP信封中使用
<xop:Include href="cid:NETRoadmap.pdf" />替代直接写cid:...,符合MTOM对二进制数据的引用规范 - 设置multipart内容的
Content-Type为multipart/related,并添加type="application/xop+xml"、start和start-info参数 - SOAP部分的
Content-Type改为application/xop+xml,并设置对应的Content-ID - 附件部分仅保留
Content-Type和Content-ID,不需要Content-Disposition(MTOM通过Content-ID关联,而非附件 disposition)
内容的提问来源于stack exchange,提问作者Ananthakrishnan Mohandas
相关产品推荐
相关产品推荐

