tags:

views:

521

answers:

1

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 that dictionary to the response stream. So it gets the encoding right, but not exactly the format, which has already be 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?

A: 

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: avrying the content-type of the response absed 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 behavior and it works.


Also check out the WCF REST Contrib library on CodePlex.

Cheeso
Cool, I'll give that a shot.
Isaac Cambron
This was a bit of work because I was using the service host from the REST starter kit, but it definitely worked. I changed it to use the request parameters instead of the Accept header. Thanks
Isaac Cambron