Request URI in MediaTypeFormatter

Topics: Web Api
Dec 16, 2011 at 6:27 PM

Hello,

I'm trying to output different formats of a type depending on the URL of the request. Up to Preview5 I did the following to get the URI in the MediaTypeFormatters OnWriteToStream-Method:

var requestUri = OperationContext.Current
                                 .IncomingMessageHeaders
                                 .To;

But with Preview6 the OperationContext.Current Property is always null. Probably because the formatter gets executed on a different thread. So what is the correct way to get the URI in the MediaTypeFormatter? Or is there an alternative to the MediaTypeFormatter which has the request as argument?

Thank you in advance.
Regards
---
Joachim 

Dec 17, 2011 at 2:11 PM

Afaik there was a sample, which would implement this behaviour by a Handler called "UriFormatExtensionMessageHandler".

Dec 19, 2011 at 6:16 PM

Hi Joachin,

Have you looked into using the QueryString media type mapping of a formatter. With this mapping, based on query string values supplied in the Uri, the appropriate mapped formatter is picked up...

thanks,

Kiran Challa

Dec 21, 2011 at 12:48 PM

Thank you for the hint. Do you have some further resources, where this mapping is documented/implemented in the source?
Regards.
---

Joachim 

Dec 22, 2011 at 9:14 AM

Don't have my source code to hand, but I remember that my OnWriteToStream takes an HttpResponseMessage obj, which contains a reference to the request message, which includes the request URI.  I use that.

Dec 23, 2011 at 8:44 AM

No, unfortunately not. See http://wcf.codeplex.com/SourceControl/changeset/view/ee192ebdfb80#WCFWebApi%2fsrc%2fMicrosoft.Net.Http.Formatting%2fSystem%2fNet%2fHttp%2fFormatting%2fMediaTypeFormatter.cs

The Signature is:

 

protected abstract void OnWriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext context);

 

Merry Chirstmas!

Dec 23, 2011 at 2:36 PM

@jrosskopf - sorry, I remembered wrong.

Okay, this is what I do...

Add a class that overrides MediaTypeMapping, and override the OnTryMatchMediaType method for the response.  In that method use the http-response arg to acccess the original request and based on that (e.g. ?format=xml or ?format=json in the request URI) set the media type name to be either "application/vnd.blah+xml" or "application/vnd.blah+json".  I can't remember the precise details of OnTryMatchMediaType, but that's basically what it's doing: selecting either the XML-based or the JSON-based media type.

Then, in OnWriteToStream I use the 3rd arg (contentHeaders) to access the selected media type name, see whether it ends in "+xml" or "+json", and act accordingly.

Andrew

Dec 25, 2011 at 8:15 AM

Hello Andrew,

thank you for the reply. For the sake of completeness I show the solution I settled with. Credits go to Alex Zeitler (http://blog.alexonasp.net). He had a solution to for wrapping an JSON response as JSONP when a callback parameter was set.

public class RazorHtmlHandler : HttpOperationHandler<HttpResponseMessage, HttpResponseMessage>
    {
        public static readonly String OUTPUT_PARAMETER_NAME = "response";
        public static readonly MediaTypeWithQualityHeaderValue HTML_MEDIA_TYPE = new MediaTypeWithQualityHeaderValue("text/html");

        public const String DEFAULT_TEMPLATE_NAME = "index.cshtml";
        public const String DEFAULT_TEMPLATE_EXTENSION = ".cshtml";
        public const String DEFAULT_RAZOR_NAME = "_RazorHtmlProcessor_Template";

        public RazorHtmlHandler() : base(OUTPUT_PARAMETER_NAME)
        { }

        protected override HttpResponseMessage OnHandle(HttpResponseMessage response)
        {
            var request = response.RequestMessage;
            var accept = request.Headers.Accept;

            if (!accept.Contains(HTML_MEDIA_TYPE))
                return response;

            var buffer = new StringBuilder();
            var currentContent = response.Content as ObjectContent;

            try
            {                
                var template = LoadTemplateForResponse(request.RequestUri, currentContent);
                var value = ReadValueFormObjectContent(currentContent);

                buffer.Append(InvokeRazorParse(template, value));
            }
            catch (Exception ex)
            {
                throw new HttpResponseException(HttpStatusCode.InternalServerError);
            }

            response.Content = new StringContent(buffer.ToString(), 
                                                 Encoding.UTF8, 
                                                 HTML_MEDIA_TYPE.MediaType);
            return response;
        }
...
}