I have an ASP.NET MVC 4 application and I want to redirect all HTTP 400 errors to my custom error page. I was searching an hours to find a solution though HTTP 400 error isn't handled like 404 error. There are many solutions that show how to escape 400(bad request error), i.e. to allow using special characters for example in url. But I wouldn't able to find some solution to catch the exception.
Please help me to catch somehow all HTTP bad requests and redirect them to my error page.
Never redirect users in case of errors, instead return a response body for the failed request. The feature of IIS (and ASP.NET) to redirect to an error page, I believe, is fundamentally wrong, incorrect, and against the HTTP specification (because then the error is being returned for the error page resource itself, not the original request. And if it's a web-browser the user has no way of retrying, because reloading the page will return the error page again, not retrying their original failed request, which is what they want).
Anyway...
A HTTP 400 response must be generated by your application code, it isn't something that will be done automatically. A bad request is typically used when informing non-human agents (i.e. web service clients, not web browsers) that their HTTP request was missing required values or had malformed values.
You can do this in MVC by having a base controller class for all of your controllers like so:
public abstract class BaseController : Controller {
protected ActionResult Http400(String message) {
Response.StatusCode = 400;
return View(message); // you need to define a view file called "Http400.aspx" (or cshtml if you're using Razor) in your application's shared views folder
}
}
so in your application logic:
public ActionResult Foobar() {
if( IsBadRequest() ) return Http400("Bad request, try again");
}
You could do something as simple as adding adding this to your web.config
<customErrors mode="RemoteOnly">
<error statusCode="400" redirect="errorpage.html"/>
</customErrors>
There is an action in my ASP.NET MVC controller that returns JSON data with a 400 Bad Request when invalid parameters are passed to the action.
[HttpDelete]
public ActionResult RemoveObject(string id) {
if(!Validate(id)) {
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { message = "Failed", description = "More details of failure" });
}
}
This works perfectly running under IIS or with the development test server launched from Visual Studio. After the project has been deployed to Azure the 400 Bad Request comes back without the JSON data. The content type has changed to 'text/html' and 'Bad Request' for the message.
Why is the behavior different under Azure?
Add the following entry to your 'web.config'.
<system.webServer>
<httpErrors existingResponse="PassThrough"/>
</system.webServer>
This will allow HTTP errors to pass through un-molested.
I have a issue in implementing Error handling in Web-APi
[HttpGet]
public UserAccount Get()
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("My Error Message"),
ReasonPhrase = "Critical Exception"
});
}
When I tried to call this method from my machine(site is hosted locally), it is working as expected. i.e I got the error message in response (plain text). But when I access the same url from another machine(LAN connected), response is IIS 500 internal error page. Here my problem is that, I am not getting the custom error message "My Error Message"
Any suggestions?
Add this to your web config:
<system.webServer>
<httpErrors errorMode="Detailed"/>
I have an asp.net MVC3 application. In my controller I have an ajax action that sets custom response code using Response.StatusCode = 600. I need to pass through the response as is without the IIS trying to look for the custom error page. I tried to use the following code to let IIS not use its custom page for response status of 600.
<!-- Pass through Ajax Errors with status code 600 -->
<httpErrors errorMode="Detailed" existingResponse="PassThrough">
<error statusCode="600" path="/" />
</httpErrors>
<!--End -->
The problem with the above snippet is that this applies to all the response codes so even if the Code fails with 500 Internal Server, the response passes through as is without IIS interfering. This exposes my internal Controller and View code to the user (if by chance some exception occurs that I have not handled).
So, how do I configure web.config to pass through detailed response only when response.statuscode is 600 (custom) and provide the default IIS custom pages for other errors (for example Internal Server Error 500).
If you are running in integrated pipeline mode you could try setting the TrySkipIisCustomErrors property to true:
Response.StatusCode = 600;
Response.TrySkipIisCustomErrors = true;
This being said the HTTP specification clearly defines that response HTTP status code are not superior to 5xx. So setting Response.StatusCode = 600; seems like something pretty unusual here. What exactly are you trying to achieve and why the standard HTTP response codes defined in the specification cannot cover your scenario?
In the web.config I set the custom error section like this:
<customErrors mode="On">
<error statusCode="404" redirect="~/Home/NotFound"></error>
</customErrors>
And then in the controllers, i still set the [HandleError] attribute above my controller. All unhandled exceptions go to the normal Error page in the shared. Any 404 errors go to the NotFound page.
I've been struggling all day to implement error handling in my ASP.NET MVC 2 app. I've looked at a variety of techniques, but none work properly. I'm using MVC2 and .NET 4.0 (started the project before MVC3 was released; we'll upgrade after we deliver our initial release).
At this point, I'll be happy to properly handle 404 and 500 errors -- 403 (authorization required) would be great, too, followed by various other specific responses. Right now, I either get all 404s, all 500s, all 302s before the 404, or all 302s before the 500.
Here are my requirements (which should be pretty close to the basic requirements of HTTP):
If a resource is not found, throw a 404, and display a 404-specific page with the requested URL. DO NOT return an intermediate response code like 302. Ideally, keep the requested URL, rather than showing a new URL like /Error/NotFound -- but if the latter displays, be sure we didn't return a redirect response to get it.
If an internal server error occurred, throw a 500, and display a 500-specific error with some indication of what went wrong. Again, don't return an intermediate response code, and ideally don't change the URL.
Here's what I'd consider a 404:
Static file not found: /Content/non-existent-dir/non-existent-file.txt
Controller not found: /non-existent-controller/Foo/666
Controller found, but Action not found: /Home/non-existent-action/666
Controller and action found, but the action can't find the requested object: /Home/Login/non-existent-id
Here's what I'd consider a 500:
Post a bad value: POST /User/New/new-user-name-too-long-for-db-column-constraint
Non-data-related problem, like a Web Service endpoint not responding
Some of these problems need to be identified by specific controllers or models, and then the controllers should throw the appropriate HttpException. The rest should be handled more generically.
For 404 case #2, I tried to use a custom ControllerFactory to throw a 404 if the controller can't be found.
For 404 case #3, I've tried to use a custom base controller to override HandleUnknownAction and throw a 404.
In both cases, I get a 302 before the 404. And, I never get 500 errors; if I modify Web.config to put a typo in my Web Service endpoint, I still get a 302, then a 404 saying the URL (controller/action) which uses the Web Service can't be found.
I also get the requested URL as a(n unwanted) querystring param: /Error/NotFound?aspxerrorpath=/Home/non-existent-action
Both of these techniques came from http://www.niksmit.com/wp/?p=17 (How to get normal 404 (Page not found) error pages using ASP.Net MVC), pointed to from http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/
If in Web.config I have <customErrors mode="On" defaultRedirect="~/Error/Unknown" redirectMode="ResponseRedirect" />, I get the appropriate response code, but my Error controller never gets called. Taking out the redirectMode attribute gets me the MVC error views, but with an intervening 302 and a changed URL -- and always the same controller (Unknown = 500; if I change it to NotFound everything looks like a 404).
Here are some of the other things I've read and tried to implement:
http://www.davidjuth.com/asp-net-mvc-error-handler.aspx
http://sanjayuttam.com/wordpress/index.php/c-sharp/c-sharp-code-examples/error-handling-in-asp-net-mvc-1-part-2-of-2/
http://blog.hebbink.com/post/2010/12/14/NET-custom-404-error-page-returns-302-for-http-status.aspx
http://blog.dantup.com/2009/04/aspnet-mvc-handleerror-attribute-custom.html
http://weblogs.asp.net/scottgu/archive/2008/07/14/asp-net-mvc-preview-4-release-part-1.aspx
.. along with a bunch of StackOverflow posts.
Seems to me this sort of error handling is pretty basic to Web apps, and the MVC framework ought to have defaults that do this out of the box, and let people extend it to work otherwise. Perhaps they'll do it in a future release. In the meantime, can someone give me comprehensive details on how to implement proper HTTP responses?
Here's one technique you could use. Define an ErrorsController which will serve the error pages:
public class ErrorsController : Controller
{
public ActionResult Http404()
{
Response.StatusCode = 404;
return Content("404", "text/plain");
}
public ActionResult Http500()
{
Response.StatusCode = 500;
return Content("500", "text/plain");
}
public ActionResult Http403()
{
Response.StatusCode = 403;
return Content("403", "text/plain");
}
}
and then in Global.asax you could subscribe for the Application_Error event where you could log the exception and execute the corresponding action of the ErrorsController:
protected void Application_Error(object sender, EventArgs e)
{
var app = (MvcApplication)sender;
var context = app.Context;
var ex = app.Server.GetLastError();
context.Response.Clear();
context.ClearError();
var httpException = ex as HttpException;
var routeData = new RouteData();
routeData.Values["controller"] = "errors";
routeData.Values["exception"] = ex;
routeData.Values["action"] = "http500";
if (httpException != null)
{
switch (httpException.GetHttpCode())
{
case 404:
routeData.Values["action"] = "http404";
break;
case 403:
routeData.Values["action"] = "http403";
break;
case 500:
routeData.Values["action"] = "http500";
break;
}
}
IController controller = new ErrorsController();
controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData));
}
And now all that's left is to start throwing proper exceptions:
public class HomeController : Controller
{
public ActionResult Index()
{
throw new HttpException(404, "NotFound");
}
}
For HTTP 404 errors (without redirects) take a look at my blog post on the subject. This might give you some good ideas:
http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16
This doesn't answer your question, but it is important to note that HTTP status 500 indicates that something went wrong on the server, so your example:
POST /User/New/new-user-name-too-long-for-db-column-constraint
Is not valid grounds to throw a 500, its a data validation issue and should be handled by MVC data annotations or a jQuery validation framework or etc. Just showing an error message next to the TextBox saying "User Name too long" is much better.
This is a very old question. but I thought It's worth it if I introduce you to a much much cleaner way to handle Http Exceptions that I saw in dear "Jesse Webb's answer".
The solution is to use the httpErrors element of the system.webServer section:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
<error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>
You also can log all exceptions in this way. "Read the "Jesse Webb's answer"".
This really feels much cleaner and also works as well as every other solution (without redirect).
Note: This only works work in IIS 7 and and newer. (Because of the httpErrors element which was recently added.