Set response status code in MediaTypeFormatter

Topics: Web Api
Nov 28, 2011 at 2:35 PM

Hello,

I'm facing some problems while implementing a custom MediaTypeFormater which converts my resource into image/gif presentation. My media type formater reads a gif file located on another server and writes it to the stream. This works well, except when the remote server is unavailable (connection problems, etc. ) I would like to set the 503 (Service Unavailable) status code for outgoing response but I cannot see anything in OnWriteToStream that allows me to do so.

There is a HttpContentHeader and TransportContext as method parameters but before (I think in preview 3) we had HttpResponseMessage which was a way simplier.

Thanks in advance for you help.


Nov 28, 2011 at 3:53 PM

I respond to my own question. After a few searches I ended up with implementing an HttpErrorHandler. So I let my formater throw an exception and then in my error handler I check for an exception type and change the response status. One drawback is that the error handlers are coupled the formatters but I don't know how to do it better. Is that the right approach ? Thanks in advance.

Coordinator
Nov 28, 2011 at 3:59 PM
You can throw HttpResponseException. It carries an HttpResponseMessage which you can set to anything you want. You should not need a custom error handler.

Sent from my Windows Phone

From: tjaskula
Sent: 11/28/2011 7:53 AM
To: Glenn Block
Subject: Re: Set response status code in MediaTypeFormatter [wcf:281055]

From: tjaskula

I respond to my own question. After a few searches I ended up with implementing an HttpErrorHandler. So I let my formater throw an exception and then in my error handler I check for an exception type and change the response status. One drawback is that the error handlers are coupled the formatters but I don't know how to do it better. Is that the right approach ? Thanks in advance.

Nov 28, 2011 at 4:04 PM

Thank you Glenn. Yes it makes sense as it seems that the custom error handler isn't invoked for exceptions occuring in formatters.

Nov 28, 2011 at 4:37 PM

I tried to do the following :

throw new HttpResponseException(HttpStatusCode.ServiceUnavailable);

or

throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.ServiceUnavailable) {ReasonPhrase = "Some message"});

but now i get the error message : "The response message returned by the Response property of this exception should be immediately returned to the client.  No further handling of the request message is required."

 

Any hints for that ?

Coordinator
Nov 28, 2011 at 5:32 PM

I am am assuming this is because you are running in the IDE. Try turning off break on all errors or at least setting VS to not break on HttpResponseException.

Coordinator
Nov 28, 2011 at 5:41 PM

tjaskula above I mentioned that you can toy with the http response message and set whatever you want. That's not entirely true. For reading content (OnReadFromStream) it will work. If however the formatter is writing a response, then you will not be able to modify headers. This is a known issue and the team is exploring how to fix it in the future. I believe status code "should" work, but let us know.

The workaround if you need to access headers would be using an operation handler or a message handler.

Thanks

Glenn

Nov 28, 2011 at 5:44 PM

I'm running in IIS and using fiddler to test. I got 504 ReadResponse failed message instead of 503.

Coordinator
Nov 28, 2011 at 5:50 PM

I see, and it worked fine when you used a custom error handler? Sounds like a bug.

Nov 28, 2011 at 5:56 PM
gblock wrote:

tjaskula above I mentioned that you can toy with the http response message and set whatever you want. That's not entirely true. For reading content (OnReadFromStream) it will work. If however the formatter is writing a response, then you will not be able to modify headers. This is a known issue and the team is exploring how to fix it in the future. I believe status code "should" work, but let us know.

The workaround if you need to access headers would be using an operation handler or a message handler.

Thanks

Glenn


Thank you Glenn for your input. You mean that I should use message handler or operation handler before the operation has been invoked ? So if I understand you well I should check if the server is available in custom message handler before the operation is run, and return with error before the operation has been hit. This could be possible, but I don't like to idea to access twice the resource over the network (once for checking the server availability and the next time to read the image). But if there is no other options for now, I'll do that. I hope your team will manage to fix this issue.

Setting just a status code in HttpResponseException throws the same exception.

Thanks,

Thomas

Nov 28, 2011 at 5:59 PM
gblock wrote:

I see, and it worked fine when you used a custom error handler? Sounds like a bug.


Custom error handler is not even run when an exception is thrown from custom media type formater (I tried with WebException).

Coordinator
Nov 28, 2011 at 6:02 PM

You can use an operation handler to access before or after the operation executes. I see your concern on accessing the resource twice, however it "should" be cached the second time if you are going through the inetcache cache.

Nov 28, 2011 at 7:56 PM

The two servers are on internal LAN and there's no cache between them. I'll try to implement Operation handler to see if it works well. So you have an idea why a custom error handler is not hit when the formatter has thrown an exception ?

Thomas

Coordinator
Nov 28, 2011 at 8:46 PM

Writing out the formatted data happens very late in process – you are basically writing out to the wire. If your formatter blows up at this point there isn’t a lot that you can do because part of the response may already have been written. You need to retrieve the image earlier in the pipeline.

Daniel Roth

Coordinator
Nov 28, 2011 at 9:22 PM

I am not talking about a separate cache server. I mean the local browser cache.

Nov 28, 2011 at 9:46 PM
danroth27 wrote:

Writing out the formatted data happens very late in process – you are basically writing out to the wire. If your formatter blows up at this point there isn’t a lot that you can do because part of the response may already have been written. You need to retrieve the image earlier in the pipeline.

 

Daniel Roth

 


Thanks for your response. What's the best design scenario for this case ? As Glenn suggested earlier I could retrieve the image in operation handler and retrieve it in the formater. Should I append it in the HttpResponseMessage or there is another more elegant solution ?

Nov 28, 2011 at 9:46 PM
gblock wrote:

I am not talking about a separate cache server. I mean the local browser cache.


Ok, it's an machine-to-machine api so I have to find another solution.

Coordinator
Nov 28, 2011 at 10:06 PM

is the image on a network share that is accessed with regular .NET IO apis, or are you accessing images on a web server using something like http client?

Nov 28, 2011 at 10:09 PM
gblock wrote:

is the image on a network share that is accessed with regular .NET IO apis, or are you accessing images on a web server using something like http client?


I'm using an http client to get the image from the othr server through http.

Coordinator
Nov 28, 2011 at 11:45 PM

OK, so HttpClient uses WebClient under the hood which I "believe" is using the browser cache. Thus it's possible for the resulting image that is retrieved to be retrieved from the cache the second time. I would verify though.

Coordinator
Nov 28, 2011 at 11:49 PM

Sorry I mean HttpClient uses HttpWebRequest not HttpClient which does (just confirmed) leverage the IE cache.

Nov 29, 2011 at 8:43 AM

Thank you for your help. Anyway, great stuff WCF Web API !

Nov 29, 2011 at 3:37 PM

Glenn,

I'm trying to make work HttpClient with a cache. I configure a HttpRequestMessage before passing it to the HttpClient like this :

var cacheControlHeader = new CacheControlHeaderValue { Public = true, MaxAge = TimeSpan.FromDays(2) }; 
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, fileUri); 
httpRequestMessage.Headers.CacheControl = cacheControlHeader; 
var resultTask = _httpClient.SendAsync(httpRequestMessage).Result;

However it has no effect. I looked into the HttpClientHandler implementation of Preview 6 and didn't find any code that uses the cache configured with the httprequestMessage.

Or I'm doing it wrong ?

Thanks,

Thomas


Nov 29, 2011 at 11:46 PM
Edited Nov 29, 2011 at 11:48 PM

There is no built in server side caching in Web API, however you can enable client side caching using the WinINet cache by doing the following.

 

            var clientHandler = new WebRequestHandler();
            clientHandler.CachePolicy = new RequestCachePolicy(RequestCacheLevel.CacheIfAvailable);  // Enable private caching
            
            var httpClient = new HttpClient(clientHandler);

            var responseMessage = httpClient.Get("foo");

You will have to set the cache control headers in your response on the server to tell WinInet cache that it is allowed to cache the response.

Nov 30, 2011 at 8:33 AM

Darrel,

Thank you for your response. After the change it doesn't use the cache anyway. I see the request hitting another server over which I have no control. Here's the response header from another server :

HTTP/1.1 200 OK

Content-Type: image/gif

Last-Modified: Mon, 27 Jul 2009 21:01:55 GMT

cache-control: max-age=86400

Date: Wed, 30 Nov 2011 08:23:09 GMT

X-Cache: HIT

X-Server: zzsrpc09

Content-Length: 20836

Age: 38370

Connection: keep-alive

Proxy-Connection: keep-alive

It seems for me that the response should be cached in WinInet cache ?

Thanks,

Thomas

Dec 1, 2011 at 11:45 AM

Do you have any ideas what could be wrong with that ? Maybe there something lacking in the header in order to enable caching in HttpClient ?

Coordinator
Dec 1, 2011 at 11:55 PM

Maybe something here can help?:

http://msdn.microsoft.com/en-us/library/6szcd7yh.aspx

Daniel Roth