MediaTypeFormatter

Topics: Web Api
Jun 9, 2011 at 8:08 PM

Hi,

I've just got a custom media type formatter working for my rest service, which I'm very pleased with!  Much simpler than the mess that was there before - thanks!

I have a couple of questions which I hope are straightforward:

1. Some of the clients for my service are unable to supply an "Accept" header, but they can append "format=<whatever>" in the query string.  How do I set things up so the query string setting will override the negotiation on the basis of the accept header?

2. Previously, the "help" page for the service had nice examples of typical response formats from the service, in json and xml format, but now it just says "unable to determine response format".  Is there any way to get that back, or would I need to create my own help service?

Thanks for your help!

Miles

Jun 10, 2011 at 1:54 AM

There is an extension method that allows you to configure a QueryStringMapping for a formatter.

formatter.AddQueryStringMapping("format", "atom", new MediaTypeHeaderValue("application/atom+xml"))

There is something planned for the help page, but I don't think it is fully baked yet.

Jun 10, 2011 at 10:36 AM
Edited Jun 10, 2011 at 5:03 PM

Brilliant - thanks for the speedy reply.  However, when I tested it in the browser, the "accept" header value overrides anything I set in the query string, or indeed in the content-type header.

I see in the source that this is because it goes through each formatter, looking first at the response content type, then the accept headers, then the media type mappings, and last of all the request content type.  Only if nothing matches will it move onto the next formatter.  I expected it to work differently:

+ firstly I expected the media type mappings to override the accept header (I am using the mappings to specify a querystring value to determine the content type) - if I've specified a content type, then that's what I expect, and I think the right response is either an error (accept and querystring headers conflict so no media formatter is selected) or (more useful IMHO) ignore the accept header and go with the mapping.

+ secondly, I expected each rule to be applied to each formatter, and only if there were no matches, to use the next.  So it would first check if a formatter supported the response content type, and if this were null or no formatter supported it, it would then check each formatter against the media type mappings.  Again, if there's no match, check each formatter against the accept header, and finally, if no matches, check against the request content type.

Is it possible to make the negotiation step pluggable in some way so it's possible to supply an implementation in the config?  FWIW, I've made a fork that makes the behaviour pluggable - https://hg01.codeplex.com/forks/mr_miles/formatterselector.

As for the the REST help page, I'm usually take-it-or-leave-it on auto-generated docs, but I have surprised myself by finding it very useful!

Keep up the great work!

Coordinator
Jun 14, 2011 at 1:10 AM
Edited Jun 14, 2011 at 1:10 AM

Thansk for the feedback Mr Miles.

The current design was to deliberately not modify the accept header with mappings as mappings don't cause any side effects. I don't think we're likely to change that as they are inherently immutable.

You could however implement a custom DelegateChannel to handle this exact logic. I have one implemented in our ContactManger_Advanced that takes a uri segment and maps it to override the accept header. This could easily be change to instead use a query string.

The code is here. If you look within you will see a UriFormatExtensionChannel that accepts a collection of mappings from uri extension to accept header. It is registered on the config via the AddMessageHandlers method on the config.

 

Coordinator
Jun 14, 2011 at 1:12 AM

As for the help page, we are investigating something in the box but we are exploring alternatives than the RPC based page we delivered in WCF 4.0. Would be interested to understand your scenarios.

Thanks

Glenn

Jun 17, 2011 at 4:39 PM

Thanks for pointing me in the direction of the DelegateChannel - I have that working nicely now via the query string, much simpler than what I'd outlined! Great!

As for the help page, I suppose there were two real scenarios that I'd found myself using it for:

1. API overview - just listing all the URIs in the interface out was a useful thing to share with others.  Typically the rest api is pretty readable, so this was good even without descriptions etc.

2. Sample formats.  Particularly for the json, the sample format was useful as I could paste it into other tools, plus also it was quite usable.

On a negative, registering different routes via global.asax splits the help up into several pages, which is a pain.  It would be useful if they could all be linked together somehow.

Cheers,

Miles