0

First off, I'm not posting this lightly. I've just spent a near-record half a day trying to solve this.

I'm developing an ASP.NET MVC website using Visual Studio 2012 on a laptop. I have implemented error-handling, which works, by adding this code to my GLOBAL.ASAX file using this approach:

protected void Application_Error(object sender, EventArgs e)
    {
        Exception exception = Server.GetLastError();
        Server.ClearError();

        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        routeData.Values.Add("action", "Index");
        routeData.Values.Add("exception", exception);

        if (exception.GetType() == typeof(HttpException))
        {
            routeData.Values.Add("statusCode", ((HttpException)exception).GetHttpCode());
        }
        else
        {
            routeData.Values.Add("statusCode", 500);
        }

        IController controller = new ErrorController();
        controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
        Response.End();
    }

I've got a view which generates an error in the Razor mark-up:

@{
            // trigger an error
            int i =1/0; 
        }

When I browse to this view, it triggers an error, taking me to controller Error, action Index, where this bit of code shows an appropriate view:

public ActionResult Index(int statusCode, Exception exception){
Response.StatusCode = statusCode;
return View(); }

So far, so good. My question is - how can I get a 404 to be handled in the same way? So if I go to a non-existent page, I get:

enter image description here

Things I've tried:

  • Every possible arrangement of CustomErrors - On / Off in web.config
  • Debugging global.asax to check it's running on application start-up
  • lots of other things, I'm sure, which I've now forgotten!

The web server being used by Visual Studio is IIS 8 Express, if that's of any help.

Andy Brown
  • 5,309
  • 4
  • 34
  • 39
  • PS This may well be a duplicate of other questions, but none of the solutions proposed works in my case, that I can see. – Andy Brown Aug 08 '14 at 12:33
  • If you are using custom errors, did you remove filters.Add(new HandleErrorAttribute()); from the FilterConfig class (if you have one)? That can screw up custom errors. – ryanulit Aug 08 '14 at 12:54

3 Answers3

0

There are plenty ways to do that.

Simplest way

Download and install this nuget - NotFoundMvc and give this blog post a read.

Or check out this beautiful answer on SO, orignally taken from Real world error hadnling in ASP.NET MVC RC2. But since the current state of the source site is in horrible design state, i ll repost the code here below.

Goal:

To catch every error that occures on server include HttpExeptions like 404 (Page not found).

The ability to choose a specific View (error Template) for some errors and a generic View/Behaviour if nor specific has been specified.

Scenario 1: anonymus user typed a wrong url. Actions: display a user friendly message like: "OOPS, The page is missing… back to home" Log the error to .log file on server. Send email to admin.

Senario 2: same as scenario 1 but one difference : user is anonymus but his IP is from our company office. Actions: show the detailed error.

Let's see some code:

protected void Application_Error(object sender, EventArgs e) {

Exception exception = Server.GetLastError();

// Log the exception.
ILogger logger = Container.Resolve < ILogger > ();
logger.Error(exception);

Response.Clear();


HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");

if (httpException == null) {
    routeData.Values.Add("action", "Index");
} else //It's an Http Exception, Let's handle it.
{

    switch (httpException.GetHttpCode()) {
        case 404:
            // Page not found.
            routeData.Values.Add("action", "HttpError404");
            break;
        case 500:
            // Server error.
            routeData.Values.Add("action", "HttpError500");
            break;
            // Here you can handle Views to other error codes.
            // I choose a General error template  
        default:
            routeData.Values.Add("action", "General");
            break;
    }
}

// Pass exception details to the target error View.
routeData.Values.Add("error", exception);

// Clear the error on server.
Server.ClearError();

// Call target Controller and pass the routeData.
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

}

Add this method to Global.asax:

Created a new controller called ErrorController:

You can specify some more behaviors to another eception types, I breated a view just for error: 404, 500.

public ActionResult HttpError404(string error) {
    ViewData["Title"] = "Sorry, an error occurred while processing your request. (404)";
    ViewData["Description"] = error;
    return View("Index");
}

public ActionResult HttpError500(string error) {
    ViewData["Title"] = "Sorry, an error occurred while processing your request. (500)";
    ViewData["Description"] = error;
    return View("Index");
}


public ActionResult General(string error) {
    ViewData["Title"] = "Sorry, an error occurred while processing your request.";
    ViewData["Description"] = error;
    return View("Index");
}
Community
  • 1
  • 1
Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281
0

You need to catch this in the routing engine of MVC. Add a route like this:

routes.MapRoute(
    name: "NotFound",
    url: "{*url}",
    defaults: new { controller = "Error", action = "Http404" }
);

Then in your ErrorController add this action:

public ActionResult Http404()
{
    Response.StatusCode = (int)HttpStatusCode.NotFound;
    return View();
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • This is what I had, but then I had problems with static files. When I'm feeling less woolly-headed (MVC routing does that to me, I find) I'll post another related question and link to it, in the hope that it will be useful for someone else too. Thanks for this so far. – Andy Brown Aug 08 '14 at 15:36
0

Yasser's link led me to an answer which seems to work. In the web.config file section, add:

   <httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
 </httpErrors>

You can then create a router and controller for this path, and it all seems to work. Is it really that simple?

Andy Brown
  • 5,309
  • 4
  • 34
  • 39
  • Just thought I'd follow up a few days down the line. The above works fine if you get rid of the CustomErrors section from the web.config file as well. Be aware that this solution worked fine for us, but it won't let you return a 404 code for missing pages. This solution has caused me many less problems than the one I was using before, which was to have a catch-all router. At some stage, when I have more time, I might blog about this in detail to highlight the many pitfalls I've come across on my painful journey! – Andy Brown Aug 11 '14 at 11:54