Supporting both Mtom and Text as MessageEncoding in WCF

kipusoep picture kipusoep · Apr 12, 2012 · Viewed 10.6k times · Source

I have a WCF service, which has a method with the following signature:

object GetCommand(Guid apiKey, SocialService service, string name, object argument);

The reason it's working with objects as both the return type and last argument, is because it should be possible to pass any type as argument and return any type.

Anyway, I'm passing an object, which contains the following property:

public byte[] Photo { get; set; }

To enable large messages, I'd like to start using Mtom, while I was previously using plain-text as MessageEncoding type.

Problem is, I want it to be backwards compatible, so current already-configures clients should keep using plain-text encoding, while new clients should be able to use Mtom via the web.config.

My question is: is it possible to keep using plain-text as MessageEncoding by default (existing clients) and offer Mtom encoding as well side-by-side?

I've tried some things with the configuration, like defining multiple endpoints with different binding-configurations, but I can't get it to work:

Server

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpTextBinding_SocialProxy" />
            <binding name="BasicHttpMtomBinding_SocialProxy" maxReceivedMessageSize="5242880" messageEncoding="Mtom">
                <readerQuotas maxStringContentLength="655360" maxArrayLength="1310720" maxNameTableCharCount="1310720" maxBytesPerRead="327680" />
            </binding>
        </basicHttpBinding>
    </bindings>
    <services>
        <service name="SocialProxyService">
            <endpoint name="BasicEndpoint_SocialProxy" address="" contract="InfoCaster.SocialProxy.ISocialProxy" binding="basicHttpBinding" bindingConfiguration="BasicHttpTextBinding_SocialProxy" />
            <endpoint name="MtomEndpoint_SocialProxy" address="" contract="InfoCaster.SocialProxy.ISocialProxy" binding="basicHttpBinding" bindingConfiguration="BasicHttpMtomBinding_SocialProxy" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior>
                <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
                <serviceMetadata httpGetEnabled="true" />
                <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

Client

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_SocialProxy" messageEncoding="Mtom" />
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://socialproxy.local/socialproxy.svc"
            binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_SocialProxy"
            contract="Webservice.SocialProxy" name="BasicHttpBinding_SocialProxy" />
    </client>
</system.serviceModel>

Problem is:

If I don't set Mtom messageEncoding @ the client, everything works via plain-text. But when I set it to use Mtom, I'll get an exception:

The remote server returned an error: (415) Cannot process the message because the content type 'multipart/related; type="application/xop+xml";start="<http://tempuri.org/0>";boundary="uuid:65a6b418-8eb3-4c76-b4c0-ea3486a56892+id=2";start-info="text/xml"' was not the expected type 'text/xml; charset=utf-8'..

Anyone able to help me out? :-)

Answer

carlosfigueira picture carlosfigueira · Apr 12, 2012

If you want to add a new endpoint (for newer clients), that endpoint needs to be in a different address. Since you didn't get any error, I imagine you have an incorrect name in the name attribute of the <service> element. Remember that the name attribute should contain the fully-qualified name of the service class. If your service class is at the namespace InfoCaster.SocialProxy, your service configuration should be defined like below:

<services> 
    <service name="InfoCaster.SocialProxy.SocialProxyService"> 
        <endpoint name="BasicEndpoint_SocialProxy" address="" contract="InfoCaster.SocialProxy.ISocialProxy" binding="basicHttpBinding" bindingConfiguration="BasicHttpTextBinding_SocialProxy" /> 
        <endpoint name="MtomEndpoint_SocialProxy" address="newClients" contract="InfoCaster.SocialProxy.ISocialProxy" binding="basicHttpBinding" bindingConfiguration="BasicHttpMtomBinding_SocialProxy" /> 
    </service> 
</services> 

And the clients would have something like

<client>  
    <endpoint address="http://socialproxy.local/socialproxy.svc/newClients"  
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_SocialProxy"  
        contract="Webservice.SocialProxy" name="BasicHttpBinding_SocialProxy" />  
</client>  

Now, if you want a single endpoint which can support both text and MTOM in a way that clients sending text receive a text response, and clients which send MTOM receive a MTOM response back, you can still do it. You'll need a custom encoder, and I wrote one in the post at http://blogs.msdn.com/b/carlosfigueira/archive/2011/02/16/using-mtom-in-a-wcf-custom-encoder.aspx.