Mixing XML and JSON in RESTful WCF without separate methods
I have a RESTful WCF service that can return XML, JSON, or JSONP, depending on the arguments, e.g. /service.svc/stuff?format=xml
or service.svc/stuff?format=json&callback=myCallback
. To do this, I've created a custom Behavior, MethodEncoder and MethodEncoderFactory which handle wrapping the JSONP callback and chooses the writer based on the format argument. In my encoder's WriteMessage()
method, I do something like
XmlWriter writer = IsXmlRequested() ? XmlDictionaryWriter.CreateTextWriter(stream) :
JsonReaderWriterFactory.CreateJsonWriter(stream)
message.WriteMessage(writer);
Then, I define my service methods as if they just return JSON but use my custom binding element:
[OperationContract, JSONPBehavior, WebGet(ResponseFormat = WebMessageFormat.Json,
UriTemplate = "stuff")
public List<Thing> GetStuff(){...}
And it almost works. When I ask for XML or JSON, I get something in the right format, but the XML isn't serialized as I expect. Here's what the XML looks like:
<root type="array">
<item type="object">
<FirstPropertyOnAThing>1</FirstPropertyOnAThing>
Whereas if I were to just set the WebMessageFormat to XML, I would get something like this:
<ArrayOfThings xmlns="...>
<Thing ...>
<FirstPropertyOnAThing>1</FirstPropertyOnAThing>
I definitely want the latter. I guess this is happening because the result is serialized to a dictionary when the Message object is created; my custom encoder is just deciding how to write 开发者_如何学Gothat dictionary to the response stream. So it gets the encoding right, but not exactly the format, which has already been decided by the ResponseFormat.
First, is that right? If so, how can I fix this? For example, can I write my own WebMessageFormat? Or do I just have to give in and write separate methods (and URI templates) that have different ResponseFormat properties for /json/* and /xml/*?
Update: In .net 4, there's a WebOperationContext.Current.OutgoingResponse.Format
property you can just set. I guess my issue boils down to: is there a way to accomplish that in .net 3.5?
Yes, there's a way to accomplish what you want in .NET 3.5, without writing separate methods.
This blog post deals with the situation you describe: varying the content-type of the response based in the incoming request. But, the post describes a slightly different approach to the solution.
In particular, the requester specifies the desired content type NOT in the request URL, but rather in the Accept header of the request.
The solution involves the use of a custom WebHttpBehavior that inspects the Accept header and formats the response appropriately. A very elegant solution, in my opinion. There's nothing you have to do in your business logic to get the adaptive formatting. Just attach the behaviour and it works.
Also check out the WCF REST Contrib library on CodePlex.
精彩评论