Can you overriding Content type mismatch and get the message anyway?
- I am writing a WCF app that talks through the WSD scan proticals to a scanner. The problem I am running into is that WCF request/response messages must be the same Content type.
In the WSD Scan RetrieveImage messages the request must be application/soap+xml; charset=utf-8 but the response coming from the scanner is a multipart/related message with MTOM embedded (the image). I get the Content type mismatch in my catch block.
The error message goes on to say that if using a custom encoder to check that the IsContentTypeSupported method is implemented correctly. I am not using a custom message encoder.
Is there a way to override this without a custom encoder? can you override the HandleReturnMessage method to do all the work yourself? Am I missing an easy solution?
I must get this working.
Thanks in advance for any help,
Eric
E Schlosser
Answers
- I think the custom encoder is your only solution here. You need the outgoing messages to be application/soap+xml (what the TextMessageEncoder generates), but the incoming messages are multipart/related (what the MtomMessageEncoder understands). Your custom encoder would wrap one of each; on the WriteMessage method, it would use the text encoder, while on ReadMessage it would use the Mtom encoder.
public class Post_480f1bc4_1fc4_40e9_a2ed_efcf3009d6ef { [ServiceContract] public interface ITest { [OperationContract] string Echo(string text); } public class Service : ITest { public string Echo(string text) { return text; } } public class MyNewEncodingBindingElement : MessageEncodingBindingElement { MessageVersion messageVersion = MessageVersion.Default; public override MessageEncoderFactory CreateMessageEncoderFactory() { return new MyNewEncoderFactory(this.messageVersion); } public override MessageVersion MessageVersion { get { return this.messageVersion; } set { this.messageVersion = value; } } public override BindingElement Clone() { return this; } public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) { context.BindingParameters.Add(this); return context.BuildInnerChannelFactory<TChannel>(); } public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) { context.BindingParameters.Add(this); return context.BuildInnerChannelListener<TChannel>(); } public override bool CanBuildChannelFactory<TChannel>(BindingContext context) { return context.CanBuildInnerChannelFactory<TChannel>(); } public override bool CanBuildChannelListener<TChannel>(BindingContext context) { return context.CanBuildInnerChannelListener<TChannel>(); } } class MyNewEncoderFactory : MessageEncoderFactory { MessageVersion messageVersion; MyNewEncoder encoder; public MyNewEncoderFactory(MessageVersion messageVersion) { this.messageVersion = messageVersion; this.encoder = new MyNewEncoder(messageVersion); } public override MessageEncoder Encoder { get { return this.encoder; } } public override MessageVersion MessageVersion { get { return this.encoder.MessageVersion; } } } class MyNewEncoder : MessageEncoder { MessageEncoder textEncoder; MessageEncoder mtomEncoder; public MyNewEncoder(MessageVersion messageVersion) { this.textEncoder = new TextMessageEncodingBindingElement(messageVersion, Encoding.UTF8).CreateMessageEncoderFactory().Encoder; this.mtomEncoder = new MtomMessageEncodingBindingElement(messageVersion, Encoding.UTF8).CreateMessageEncoderFactory().Encoder; } public override string ContentType { get { return this.textEncoder.ContentType; } } public override string MediaType { get { return this.textEncoder.MediaType; } } public override MessageVersion MessageVersion { get { return this.textEncoder.MessageVersion; } } public override bool IsContentTypeSupported(string contentType) { return this.mtomEncoder.IsContentTypeSupported(contentType); } public override T GetProperty<T>() { T result = this.textEncoder.GetProperty<T>(); if (result == null) { result = this.mtomEncoder.GetProperty<T>(); } if (result == null) { result = base.GetProperty<T>(); } return result; } public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType) { return this.mtomEncoder.ReadMessage(buffer, bufferManager, contentType); } public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) { return this.mtomEncoder.ReadMessage(stream, maxSizeOfHeaders, contentType); } public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) { return this.textEncoder.WriteMessage(message, maxMessageSize, bufferManager, messageOffset); } public override void WriteMessage(Message message, Stream stream) { this.textEncoder.WriteMessage(message, stream); } } static Binding GetBinding(bool useCustomEncoder) { if (useCustomEncoder) { return new CustomBinding( new MyNewEncodingBindingElement(), new HttpTransportBindingElement()); } else { return new CustomBinding( new MtomMessageEncodingBindingElement(), new HttpTransportBindingElement()); } } public static void Test() { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); host.AddServiceEndpoint(typeof(ITest), GetBinding(false), ""); host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); host.Open(); Console.WriteLine("Host opened"); ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(true), new EndpointAddress(baseAddress)); ITest proxy = factory.CreateChannel(); Console.WriteLine(proxy.Echo("Hello")); ((IClientChannel)proxy).Close(); factory.Close(); Console.Write("Press ENTER to close the host"); Console.ReadLine(); host.Close(); } }- Proposed As Answer byQuan Shan - MSFTModeratorThursday, September 03, 2009 10:44 AM
- Marked As Answer byQuan Shan - MSFTModeratorTuesday, September 08, 2009 5:34 AM
- Carlos,everyone, This last issue turned out it was a FW defect from the scanner, not creating the mime boundry delimiter correctly. As for the rest of the code, I had to change the ReadQuota.MaxArrayLength = int.MaxValue. Added it right below the MaxBufferSize change (see above) and now everything works Great! Thank you Carlos! Eric
E Schlosser- Marked As Answer byeSchlosser Thursday, September 24, 2009 6:21 PM
All Replies
- I think the custom encoder is your only solution here. You need the outgoing messages to be application/soap+xml (what the TextMessageEncoder generates), but the incoming messages are multipart/related (what the MtomMessageEncoder understands). Your custom encoder would wrap one of each; on the WriteMessage method, it would use the text encoder, while on ReadMessage it would use the Mtom encoder.
public class Post_480f1bc4_1fc4_40e9_a2ed_efcf3009d6ef { [ServiceContract] public interface ITest { [OperationContract] string Echo(string text); } public class Service : ITest { public string Echo(string text) { return text; } } public class MyNewEncodingBindingElement : MessageEncodingBindingElement { MessageVersion messageVersion = MessageVersion.Default; public override MessageEncoderFactory CreateMessageEncoderFactory() { return new MyNewEncoderFactory(this.messageVersion); } public override MessageVersion MessageVersion { get { return this.messageVersion; } set { this.messageVersion = value; } } public override BindingElement Clone() { return this; } public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) { context.BindingParameters.Add(this); return context.BuildInnerChannelFactory<TChannel>(); } public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) { context.BindingParameters.Add(this); return context.BuildInnerChannelListener<TChannel>(); } public override bool CanBuildChannelFactory<TChannel>(BindingContext context) { return context.CanBuildInnerChannelFactory<TChannel>(); } public override bool CanBuildChannelListener<TChannel>(BindingContext context) { return context.CanBuildInnerChannelListener<TChannel>(); } } class MyNewEncoderFactory : MessageEncoderFactory { MessageVersion messageVersion; MyNewEncoder encoder; public MyNewEncoderFactory(MessageVersion messageVersion) { this.messageVersion = messageVersion; this.encoder = new MyNewEncoder(messageVersion); } public override MessageEncoder Encoder { get { return this.encoder; } } public override MessageVersion MessageVersion { get { return this.encoder.MessageVersion; } } } class MyNewEncoder : MessageEncoder { MessageEncoder textEncoder; MessageEncoder mtomEncoder; public MyNewEncoder(MessageVersion messageVersion) { this.textEncoder = new TextMessageEncodingBindingElement(messageVersion, Encoding.UTF8).CreateMessageEncoderFactory().Encoder; this.mtomEncoder = new MtomMessageEncodingBindingElement(messageVersion, Encoding.UTF8).CreateMessageEncoderFactory().Encoder; } public override string ContentType { get { return this.textEncoder.ContentType; } } public override string MediaType { get { return this.textEncoder.MediaType; } } public override MessageVersion MessageVersion { get { return this.textEncoder.MessageVersion; } } public override bool IsContentTypeSupported(string contentType) { return this.mtomEncoder.IsContentTypeSupported(contentType); } public override T GetProperty<T>() { T result = this.textEncoder.GetProperty<T>(); if (result == null) { result = this.mtomEncoder.GetProperty<T>(); } if (result == null) { result = base.GetProperty<T>(); } return result; } public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType) { return this.mtomEncoder.ReadMessage(buffer, bufferManager, contentType); } public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) { return this.mtomEncoder.ReadMessage(stream, maxSizeOfHeaders, contentType); } public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) { return this.textEncoder.WriteMessage(message, maxMessageSize, bufferManager, messageOffset); } public override void WriteMessage(Message message, Stream stream) { this.textEncoder.WriteMessage(message, stream); } } static Binding GetBinding(bool useCustomEncoder) { if (useCustomEncoder) { return new CustomBinding( new MyNewEncodingBindingElement(), new HttpTransportBindingElement()); } else { return new CustomBinding( new MtomMessageEncodingBindingElement(), new HttpTransportBindingElement()); } } public static void Test() { string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); host.AddServiceEndpoint(typeof(ITest), GetBinding(false), ""); host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); host.Open(); Console.WriteLine("Host opened"); ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(true), new EndpointAddress(baseAddress)); ITest proxy = factory.CreateChannel(); Console.WriteLine(proxy.Echo("Hello")); ((IClientChannel)proxy).Close(); factory.Close(); Console.Write("Press ENTER to close the host"); Console.ReadLine(); host.Close(); } }- Proposed As Answer byQuan Shan - MSFTModeratorThursday, September 03, 2009 10:44 AM
- Marked As Answer byQuan Shan - MSFTModeratorTuesday, September 08, 2009 5:34 AM
Carlos,
Thank you!. I will plug this in and let you know.
Eric
E Schlosser- Carlos,
I have got this working up to the point that I am recieveing mtom data back from the scanner. I then recieve an error stating that "The Maximum message quota for the incoming messages (65536) has been exceeded."
Since this code build the custom binding on the fly, I can't find a place to set the MaxRecievedMessageSize property.
Any help would be greatly appriciated.
Eric
E Schlosser You can set it on the HttpTransportBindingElement:
static Binding GetBinding(bool useCustomEncoder) { if (useCustomEncoder) { return new CustomBinding( new MyNewEncodingBindingElement(), new HttpTransportBindingElement { MaxReceivedMessageSize = int.MaxValue }); } else { return new CustomBinding( new MtomMessageEncodingBindingElement(), new HttpTransportBindingElement { MaxReceivedMessageSize = int.MaxValue }); } }Ok That was simple, But I get the same error almost immediately in the override Message ReadMessage method.
I actually get a “Error creating a reader for the MTOM Message” but the inner exception is almost the same as before, “The maximum buffer size (65536) has been exceeded…"Instead of your way
return new CustomBinding(
new MyNewEncodingBindingElement(),
new HttpTransportBindingElement { MaxReceivedMessageSize = int.MaxValue });
I tried this
CustomBibding CB = new CustomBinding(
new MyNewEncodingBindingElement(),
new HttpTransportBindingElement () );
CB.Elements.Find<TransportBindingElement>(). MaxReceivedMessageSize = int.MaxValue;
and got the same result. Is there another way of setting it?
Thank you for your Help inadvance,
Eric
E Schlosser- You may need to increase MaxBufferSize as well:
static Binding GetBinding(bool useCustomEncoder) { HttpTransportBindingElement httpBE = new HttpTransportBindingElement(); httpBE.MaxBufferSize = int.MaxValue; httpBE.MaxReceivedMessageSize = int.MaxValue; if (useCustomEncoder) { return new CustomBinding( new MyNewEncodingBindingElement(), httpBE); } else { return new CustomBinding( new MtomMessageEncodingBindingElement(), httpBE); } } - Carlos,
Still the same problem. I think the problem lies in the fact that the code inside the ELSE block that cerates the MTOM binding never gets executed. As far as I can see GetBinding only gets called with a True value.
That begs to ask, how is the MTOM binding getting created?
UpDate: I found where to fix this, it is the MyNewEncoder method. same fix as above but in that location. Got a new problem but Im working on it.
E Schlosser - Carlos,
I am still having one problem. It is in deserializing the Mime document. The reply coming in on the wire looks like this…
<body>
<RetrieveImageResponse>
<ScanData>
<xop:Include Href =”cid:1@body”></xop:include>
</ScanData>
</RetrieveImageResponse>
</body>
..7d..—993c23400-1dd2-11b2-8d4e-e93374a1c48e..Content-Type: image/jfif..Content-Transfer-Encoding: binary..Content-ID: <1@body> data
My current Message Contract looks as follows
[MessageContract(IsWrapped = false)]
public class RetrieveImageResponseMessage
{
[MessageBodyMember(Name = "RetrieveImageResponse", Namespace = StringConst.WSDScanNamespace)]
public RetrieveImageResponseType RetrieveImageResponse;
}
[XmlRoot("RetrieveImageResponse", Namespace = StringConst.WSDScanNamespace)]
public class RetrieveImageResponsetype
{
[XmlElement("ScanData")]
public byte[] ScanData;
[XmlAnyElement]
public XmlElement[] AnyElements;
[XmlAnyAttributeAttribute()]
public XmlAttribute[] AnyAttributes;
}
I have rearanged this many different ways. I have used DataContracts. I have tried several combinations. All to no avail.
I need the Content-Type And the binary data. Any ideas?
Eric
- Carlos,everyone, This last issue turned out it was a FW defect from the scanner, not creating the mime boundry delimiter correctly. As for the rest of the code, I had to change the ReadQuota.MaxArrayLength = int.MaxValue. Added it right below the MaxBufferSize change (see above) and now everything works Great! Thank you Carlos! Eric
E Schlosser- Marked As Answer byeSchlosser Thursday, September 24, 2009 6:21 PM
- Hi Carlos,
I wrote a similar version for VB and called it MixedBindingEelement. My curent config looks like this:
<bindingElementExtensions><add name="MixedEncoding" type="MixedBindingEelement, MixedEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /></bindingElementExtensions>
and this:
<wsHttpBinding><binding name="AttachmentServiceSoapBinding" maxReceivedMessageSize="67108864" messageEncoding="Mtom"><readerQuotas maxArrayLength="67108864" /></binding></wsHttpBinding>
How to I tell it to use the custom encoder?
Dave
- Hi Dave,
Take a look at the WCF Samples http://go.microsoft.com/fwlink/?LinkId=87352 , specifically:
Samples\WCFWFCardSpace\WCF\Extensibility\MessageEncoder\Text\CS\CustomTextMessageEncoder.sln
This will show you everything that needs to be created in order to use a custom binding and encoding as well as the capability to use it in your config file. Below is my config file for the custom binding I created.<system.serviceModel> <client> <endpoint address="http://10.8.28.11:8080/imageupload/upload" binding="customBinding" bindingConfiguration="MixedMessageBinding" contract="Services.Soap.FileUpload.FileUploadPortType" name="FileUploadPort"/> </client> <bindings> <customBinding> <binding name="MixedMessageBinding"> <mixedMessageEncoding messageVersion="Soap12" encoding="utf-8"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/> </mixedMessageEncoding> <httpTransport manualAddressing="false" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" allowCookies="false" authenticationScheme="Anonymous" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous" realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false" useDefaultWebProxy="true" /> </binding> </customBinding> </bindings> <extensions> <bindingElementExtensions> <add name="mixedMessageEncoding" type="WCF.MixedMessageEncodingElement, WcfMixedMessageEncoder"/> </bindingElementExtensions> </extensions> </system.serviceModel>
- Thanks aurealus2 Looking at your example and the samples finally got me to a resolution. I needed one of these:
' Binding Extension
Public Class MixedMessageEncodingExtension
Inherits ServiceModel.Configuration.BindingElementExtensionElement
Public Sub New()
MyBase.New()
End Sub
Public Overrides ReadOnly Property BindingElementType() As System.Type
Get
Return GetType(MixedEncodingBindingEelement)
End Get
End Property
Protected Overrides Function CreateBindingElement() As System.ServiceModel.Channels.BindingElement
Dim bindingElement As New MixedEncodingBindingEelement
Me.ApplyConfiguration(bindingElement)
Return bindingElement
End Function
End Class


