WCF服务接收MTOM附件POST请求报错:创建MTOM消息读取器失败
问题背景
开发了一个WCF服务,用于接收带MTOM文件附件的SOAP POST请求,服务契约及实现代码如下:
[ServiceContract(Namespace = "http://example.com/schema/service/Upload/1.0")] [XmlSerializerFormat] public interface IBillerReconcileNotificationService { [OperationContract(Action = "Reconcile")] [FaultContract(typeof(BillerNotificationFault))] UploadMsgRs Reconcile(UploadMsgRq requestModel); } public class BillerReconcileNotificationService : IBillerReconcileNotificationService { public UploadMsgRs Reconcile(UploadMsgRq requestModel) {//处理请求业务逻辑} }
使用Postman测试时,服务始终返回400 Bad Request,WCF跟踪日志抛出异常:
<Exception> <ExceptionType>System.ServiceModel.CommunicationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType> <Message>Error creating a reader for the MTOM message</Message> <StackTrace> at System.ServiceModel.Channels.MtomMessageEncoder.MtomBufferedMessageData.TakeXmlReader() at System.ServiceModel.Channels.BufferedMessageData.GetMessageReader() at System.ServiceModel.Channels.BufferedMessage..ctor(IBufferedMessageData messageData, RecycledMessageState recycledMessageState, Boolean[] understoodHeaders, Boolean understoodHeadersModified) at System.ServiceModel.Channels.MtomMessageEncoder.ReadMessage(ArraySegment`1 buffer, BufferManager bufferManager, String contentType) at System.ServiceModel.Channels.HttpInput.DecodeBufferedMessage(ArraySegment`1 buffer, Stream inputStream) at System.ServiceModel.Channels.HttpInput.ParseMessageAsyncResult.ContinueReading(Int32 bytesRead) at System.ServiceModel.Channels.HttpInput.ParseMessageAsyncResult.DecodeBufferedMessageAsync() at System.ServiceModel.Channels.HttpInput.ParseMessageAsyncResult.BeginParse() at System.ServiceModel.Channels.HttpInput.BeginParseIncomingMessage(HttpRequestMessage httpRequestMessage, AsyncCallback callback, Object state) at System.ServiceModel.Channels.HttpPipeline.EmptyHttpPipeline.BeginParseIncomingMessage(AsyncCallback asynCallback, Object state) at System.ServiceModel.Channels.HttpPipeline.EnqueueMessageAsyncResult..ctor(ReplyChannelAcceptor acceptor, Action dequeuedCallback, HttpPipeline pipeline, AsyncCallback callback, Object state) at System.ServiceModel.Channels.HttpPipeline.EmptyHttpPipeline.BeginProcessInboundRequest(ReplyChannelAcceptor replyChannelAcceptor, Action dequeuedCallback, AsyncCallback callback, Object state) at System.ServiceModel.Channels.HttpChannelListener`1.HttpContextReceivedAsyncResult`1.ProcessHttpContextAsync() at System.ServiceModel.Channels.HttpChannelListener`1.BeginHttpContextReceived(HttpRequestContext context, Action acceptorCallback, AsyncCallback callback, Object state) at System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result) at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest() at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest() at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state) at System.ServiceModel.AspNetPartialTrustHelpers.PartialTrustInvoke(ContextCallback callback, Object state) at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequestWithFlow(Object state) at System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped) at System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP) </StackTrace> <ExceptionString>System.ServiceModel.CommunicationException: Error creating a reader for the MTOM message ---> System.Xml.XmlException: Root MIME part containing infoset not found. at System.Xml.XmlMtomReader.ReadRootMimePart() at System.Xml.XmlMtomReader.Initialize(Stream stream, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize) at System.Xml.XmlMtomReader.SetInput(Stream stream, Encoding[] encodings, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize, OnXmlDictionaryReaderClose onClose) at System.ServiceModel.Channels.MtomMessageEncoder.MtomBufferedMessageData.TakeXmlReader() --- End of inner exception stack trace ---</ExceptionString> <InnerException> <ExceptionType>System.Xml.XmlException, System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType> <Message>Root MIME part containing infoset not found.</Message> <StackTrace> at System.Xml.XmlMtomReader.ReadRootMimePart() at System.Xml.XmlMtomReader.Initialize(Stream stream, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize) at System.Xml.XmlMtomReader.SetInput(Stream stream, Encoding[] encodings, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize, OnXmlDictionaryReaderClose onClose) at System.ServiceModel.Channels.MtomMessageEncoder.MtomBufferedMessageData.TakeXmlReader() </StackTrace> <ExceptionString>System.Xml.XmlException: Root MIME part containing infoset not found. at System.Xml.XmlMtomReader.ReadRootMimePart() at System.Xml.XmlMtomReader.Initialize(Stream stream, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize) at System.Xml.XmlMtomReader.SetInput(Stream stream, Encoding[] encodings, String contentType, XmlDictionaryReaderQuotas quotas, Int32 maxBufferSize, OnXmlDictionaryReaderClose onClose) at System.ServiceModel.Channels.MtomMessageEncoder.MtomBufferedMessageData.TakeXmlReader()</ExceptionString> </InnerException> </Exception>
请求头的Content-Type为:multipart/related; boundary=3f9d206f-1054-4731-b737-6e724723832a; start-info="text/xml"; type="application/xop+xml"
服务采用basicHttpBinding并启用MTOM,配置如下:
<system.serviceModel> <services> <service name="BPNotificationWeb.Services.BillerReconcileNotificationService" behaviorConfiguration="ReconcileHttpBindingBehavior"> <endpoint address="" binding="basicHttpBinding" contract="BPNotificationWeb.Services.IBillerReconcileNotificationService" bindingConfiguration="httpBinding" </endpoint> </service> </services> <bindings> <basicHttpBinding> <binding name="httpBinding" messageEncoding="Mtom" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"></binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="BillerNotificationHttpBindingBehavior"> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel>
解决建议
1. 修正服务配置的行为名称不匹配问题
服务节点的behaviorConfiguration指定为ReconcileHttpBindingBehavior,但behaviors节点下定义的行为名称是BillerNotificationHttpBindingBehavior,两者不匹配。修改服务节点的行为配置名称:
<service name="BPNotificationWeb.Services.BillerReconcileNotificationService" behaviorConfiguration="BillerNotificationHttpBindingBehavior">
2. 确保Postman请求的MTOM格式符合规范
异常提示Root MIME part containing infoset not found,说明请求的MIME结构不符合MTOM要求:
- SOAP信封必须作为第一个MIME部分(Root MIME part),该部分的Content-Type需设置为
application/xop+xml - 附件部分需通过
cid:引用关联到SOAP信封中的对应元素 - 若请求头指定了
start参数,必须与SOAP部分的Content-ID一致;未指定时,默认第一个MIME部分为Root
3. 验证数据契约的MTOM支持
确保UploadMsgRq中接收附件的字段标记了正确的特性(如[MessageMember]或[XmlElement]),类型为Stream或byte[],同时确认数据契约与SOAP信封的映射关系正确。
4. 补充绑定的readerQuotas配置
在basicHttpBinding中添加readerQuotas配置,避免因数据大小限制导致解析失败:
<binding name="httpBinding" messageEncoding="Mtom" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"> <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" /> </binding>
5. 用WCF客户端生成工具验证请求格式
通过SvcUtil.exe生成客户端代码,调用服务并抓取正确的MTOM请求格式,对比Postman构造的请求,排查格式差异。
内容的提问来源于stack exchange,提问作者Voice Of The Rain

