Last post Feb 25, 2012 02:33 AM by BriceWilson
Feb 23, 2012 04:48 AM|BriceWilson|LINK
If I add an MVC 4 Web API controller to a new MVC 4 Internet Application with forms authentication, I get an error if I try to send a 401 (HttpStatusCode.Unauthorized) back to the client using HttpResponseMessage<T>. The exception that gets thrown is:
System.Web.HttpException: Cannot redirect after HTTP headers have been sent.
It is trying to redirect the user to the login form defined in the web.config. It successfully does the redirection if I use the [Authorize] attribute on the action instead of manually setting the 401 status on the HttpResponseMethod object. However, I don't
want to use the [Authorize] attribute because I don't actually want the client redirected to the login form since it is a request to a service method.
By setting the status code myself I was hoping to employ a technique similar to that described by Phil Haack in the following blog post to prevent redirection to the login page.
Here are the steps to reproduce the error:
1. Create a new MVC 4 Internet Application
2. Add an "API" folder to the project
3. Add a new "Web API Controller Class" item to the new API folder.
4. Change the default Get() method on the new controller to the following:
public HttpResponseMessage<string> Get()
return new HttpResponseMessage<string>(System.Net.HttpStatusCode.Unauthorized);
5. Run the app and call the Get() method from the URL in a browser.
Any ideas? Am I doing something wrong or is the HttpResponseMessage object not behaving as one might expect it to.
Feb 24, 2012 10:14 PM|sirkirby|LINK
I guess this bug made its way into the beta as well. Basically, asp.net should not be attempting to redirect to the login page for ApiController unauthorized response. There is a less than ideal work-around that i was using in preview 6 which uses an http
module to intercept the response when a custom http header is added. This is a nuget package that will help which does what i'm describing http://nuget.org/packages/aspnet.suppressformsredirect.
I'm dealing with this myself as i'm porting my code, so i really hope that this gets addressed before the finaly release... or there is a better way around this that we just don't know about yet. This is a very common scenario of which there were many blog
posts about during the wcf preview, so i'm pretty suprised that it wasn't addressed.
Feb 24, 2012 10:46 PM|sirkirby|LINK
If you use a custom Message Handler, you can avoid this issue entirely. ex here
http://www.asp.net/web-api/overview/working-with-http/http-message-handlers. This of course is ideal if you want to check the header for authorization, which is what i'm doing, or some other type of message manipulation before it gets to the controller.
So, this makes sense as the best place to put it. Really, with this extensibility point you shouldn't need to return a 401 from your action method.
Feb 25, 2012 02:33 AM|BriceWilson|LINK
Thanks for the response. I think I have it working, but it took a combination of both of your suggestions. I tried using the Nuget package (http://nuget.org/packages/aspnet.suppressformsredirect)
a couple of days ago, but had the same problem when it changed the HttpStatusCode to 401. I would get an exception saying I could not redirect after headers had been sent. I removed that code and wrote a custom message handler as described in the other link
you gave me (http://www.asp.net/web-api/overview/working-with-http/http-message-handlers). I checked for an authorized message before it even made it to my controller
and immediately sent back a 401 if it wasn't. I did not get any exceptions, but I was still redirected to the login page. At that point I added the code from the above Nuget package back to my project and it rewrote the 302 redirect to a 401 unauthorized and
the request didn't get redirected and I didn't get an exception about changing the status after sending the headers.
Basically, neither suggestion worked by itself, but when I did both of them I got the expected behavior. It seems to me there is definitely a problem with the HttpResponseMessage object or how it send the response back through the message pipeline. The version
I have that now works uses the non-generic version of HttpResponseMessage. I get the exception when sending back HttpResponseMessage<T>.
Let's hope it gets fixed before the final bits ship. Thanks for the suggestions.