Here’s one for you googlers (or bingites): Using the XmlSerializerFormat attribute on methods that you expose over WCF Rest (i.e. services using the webHttpBinding) is not supported if your service is running in Medium Trust (as it most likely is if you’re using Shared Hosting, like Rackspace Cloud Sites).
This is contrary to what you might expect from reading documents like this, which say that XmlSerializer is supported in partial trust. I guess they forgot to test the webHttpBinding scenario. I should be clear: webHttpBinding itself works fine in Medium Trust: it’s using XmlSerializerFormat with webHttpBinding that will give you problems.
That’s the short story.
Here’s the amplified version.
How I learned: the hard way
I’m building a web service that my friends running MacOSX want to be able to use. Out in the Apple orchards they’re not so keen on SOAP as we are who sit behind Windows. So I turned to WCF Web Http, now part of .Net 4.0, which lets you take control of your urls and keep your messages to the bare necessities.
With WCF REST you have the choice of XML or JSON. I chose XML, and by default WCF will use the DataContractSerializer. That produces reasonably clean XML, but it is picky about ordering of elements within messages, it annotates elements with type attributes that are often unnecessary, and it has a rather clunky syntax for serializing arrays.
Then I read about the XmlSerializerFormat attribute. If you apply that to your Operation Contracts, it switches to using the XmlSerializer, and then, by decorating your data contracts with the appropriate attributes, you can take complete control of the xml which gets written to the wire.
All well and good – while the service was running on my machine. But when I pushed the bits to our web host, who run all websites under Medium Trust, it was a different story. All the calls to the service threw a ProtocolException with the helpful message: “(400) Bad Request”.
Turning on includeExceptionDetailInFaults, then spying on the interaction using Fiddler shed more light on the situation. I was getting back a stack trace with this at the top:
at System.ServiceModel.Dispatcher.UnwrappedTypesXmlSerializerManager.BuildSerializers()
at System.ServiceModel.Dispatcher.UnwrappedTypesXmlSerializerManager.GetOperationSerializers(Object key)
at System.ServiceModel.Dispatcher.SingleBodyParameterXmlSerializerMessageFormatter.EnsureSerializers()
at System.ServiceModel.Dispatcher.SingleBodyParameterXmlSerializerMessageFormatter.GetInputSerializers()
at System.ServiceModel.Dispatcher.SingleBodyParameterMessageFormatter.ReadObject(Message message)
at System.ServiceModel.Dispatcher.SingleBodyParameterMessageFormatter.DeserializeRequest(Message message, Object[] parameters)
A bit of hunting around using Reflector revealed that the BuildSerializers method always calls XmlSerializer.FromMappings, and if you check that out on MSDN, you’ll see that it requires Full Trust.
So there’s no getting away from it. You can’t use XmlSerializer together with WebHttpBinding under Medium Trust. I did try the usual work-around for getting XmlSerializer to work in Medium Trust – pre-generating the serialization assemblies at build time: but that was before Reflector showed me the clinching evidence that my efforts were futile.
So what are your options? Either stick with DataContractSerializer, or switch to JSON1. Or, if time is on your side, wait for the next version of WCF which has a new extensible Web API specially designed to let you take complete control of your message formats and such like.
- Decorate your methods like this to switch to JSON format:
[ServiceContract()] public interface IService { [OperationContract] [WebInvoke(UriTemplate = "/MyOperation", Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] Response MyOperation(Dto dto); }
2 comments:
JSON seems like a good transport format anyway, sometimes all the XML infraestructure of .NET becomes...counterproductive
Holy Shit...... For me works fine as i self-host.. However for windows mobile i have to use that serializer... Switcing to JSON might work..
Post a Comment