ASP.NET MVC 5 Custom Error Page

走远了吗. 提交于 2019-11-28 17:02:56
ubik404

[1]: Remove all 'customErrors' & 'httpErrors' from Web.config

[2]: Check 'App_Start/FilterConfig.cs' looks like this:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

[3]: in 'Global.asax' add this method:

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

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

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

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

[4]: Add 'Controllers/ErrorPageController.cs'

public class ErrorPageController : Controller
{
    public ActionResult Error(int statusCode, Exception exception)
    {
         Response.StatusCode = statusCode;
         ViewBag.StatusCode = statusCode + " Error";
         return View();
    }
}

[5]: in 'Views/Shared/Error.cshtml'

@model System.Web.Mvc.HandleErrorInfo
@{
    ViewBag.Title = (!String.IsNullOrEmpty(ViewBag.StatusCode)) ? ViewBag.StatusCode : "500 Error";
}

<h1 class="error">@(!String.IsNullOrEmpty(ViewBag.StatusCode) ? ViewBag.StatusCode : "500 Error"):</h1>

//@Model.ActionName
//@Model.ControllerName
//@Model.Exception.Message
//@Model.Exception.StackTrace

:D

Thanks everyone, but problem is not with 403 code. Actually the problem was with the way i was trying to return 403. I just changed my code to throw an HttpException instead of returning the HttpStatusCodeResult and every things works now. I can return any HTTP status code by throwing HttpException exception and my customErrors configuration catches all of them. May be HttpStatusCodeResult is not doing the exact job I expected it to do.

I just replaced

context.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);

with

throw new HttpException((int)System.Net.HttpStatusCode.Forbidden, "Forbidden");

That's it.

Happy coding.

Dush

I also had this issue. Code in the OP’s question is perfectly working except the custom error code in <system.web> section in the web.config file. To fix the issue what I need to do was add the following code to <system.webServer>. Note that ‘webserver’ instead of ‘web’.

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

If someone is using following environment, here is the complete solution:

The Environment:

  • Visual Studio 2013 Update 4
  • Microsoft .NET Framework 4.5.1 with ASP.NET MVC 5
  • Project: ASP.NET Web Application with MVC & Authentication: Individual User Account template

Custom Attribute class:

Add the following class to your web site’s default namespace. The reason explained here in the accepted answer Stack Overflow question: Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);

        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
} 

Then add the following code the web.config file

<system.webServer>
   <httpErrors errorMode="Custom" existingResponse="Replace">
      <remove statusCode="403" />
      <error statusCode="403" responseMode="ExecuteURL" path="/Error/UnAuthorized" />
   </httpErrors>
</system.webServer>

Following article explain more about this: ASP.NET MVC: Improving the Authorize Attribute (403 Forbidden)

And httpErrors in web.config section in this article: Demystifying ASP.NET MVC 5 Error Pages and Error Logging

Then add the ErrorController.cs to Controllers folder

public class ErrorController : Controller
{
    // GET: UnAuthorized
    public ActionResult UnAuthorized()
    {
        return View();
    }

    public ActionResult Error()
    {
        return View();
    }

}

Then add a UnAuthorized.cshtml to View/Shared folder

@{
    ViewBag.Title = "Your Request Unauthorized !"; //Customise as required
 }
 <h2>@ViewBag.Title.</h2> 

This will show customised error page instead of browser generated error page.

Also note that for the above environment, it is not required to comment the code inside RegisterGlobalFilters method added by the template as suggested in one of the answers.

Please note that I just cut and paste code from my working project therefore I used Unauthorized instead OP’s NoPermissions in the above code.

since I ran into a very similar issue I wanted to shed more light on it.

customErrors will only capture actual http exceptions thrown in your ASP.NET application. The HttpStatusCodeResult doesn't throw an exception though. It just writes a response with the according status code, which makes more sense in your example.

If you are running on IIS 7.0 or higher you should be using httpErrors now, as this will show you custom error pages in all cases. This is an IIS level setting.

I wrote a whole blog post about this to explain the differences: http://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging

ryanulit

Update

You only need to do that special redirect for 403 errors. All other 500 errors should take effect through your defaultRedirect="/Error/Error" setting in customErrors. However, you need to remove or comment out the HandleErrorAttribute registration in the App_Start/FilterConfig.cs file for custom errors to actually work. Otherwise, that attribute will redirect all errors to the Error.cshtml file in the Views/Shared directory.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // remove this line below
        //filters.Add(new HandleErrorAttribute());
    }
}

Original Answer

As far as I know, you cannot use customErrors in the web.config to handle 403 errors for some reason. I feel your pain as it seems like something that should be as simple as the code you already have, but apparently 403 errors are treated as a web server concern.

What you can do instead is just redirect the user to your desired "NoPermissions" page like this:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext context)
    {
        if (context.HttpContext.Request.IsAuthenticated)
        {
            context.Result = new RedirectToRouteResult(new RouteValueDictionary(new
            {
                action = "NoPermissions",
                controller = "Error",
                area = ""
            }));
        }
        else
        {
            base.HandleUnauthorizedRequest(context);
        }
    }
}

The request will have a 200 status code instead of a 403, but if you can live with that, this is an easy workaround.

Here is a similar SO question for more info: Returning custom errors.

Also, this article explains how to go the IIS route: http://kitsula.com/Article/MVC-Custom-Error-Pages

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!