正确处理ASP.NET MVC中的自定义错误?

时间:2021-05-20 04:12:55

I've spent the day going through a dozen or more sites, blogs, and SO answers, all claiming to provide the "right" way to implement custom error handling, in ASP.NET MVC.

我花了一天时间浏览了十几个或更多站点,博客和SO答案,他们都声称在ASP.NET MVC中提供了“正确”的方式来实现自定义错误处理。

None of them agree with each other, and most of them don't seem to work.

他们都没有达成一致,而且他们中的大多数似乎都不起作用。

What I want:

我想要的是:

  • Custom pages for the standard HTTP errors
  • 标准HTTP错误的自定义页面

  • That have the same look-and-feel as the standard pages of my site
  • 这与我网站的标准页面具有相同的外观

  • Without having to completely redo everything I have in my Shared/_Layout.cspx
  • 无需完全重做我在Shared / _Layout.cspx中的所有内容

  • That work whether the user is authenticated or not
  • 无论用户是否经过身份验证,都可以正常工作

  • But that don't provide access to anything the user should be required to be authenticated to access
  • 但是,这不提供对用户应该被要求进行身份验证以访问的任何内容的访问权限

  • That should properly return the URL requested with the appropriate HTTP error code, rather than redirecting to a different page that returns HTTP 200
  • 这应该正确地返回使用适当的HTTP错误代码请求的URL,而不是重定向到返回HTTP 200的其他页面

  • That redirects anything that isn't a valid route to the 404 page, whether it is a missing action, a missing controller, or a missing route
  • 这会将任何不是有效路由的内容重定向到404页面,无论是缺少操作,缺少控制器还是缺少路由

  • That allows for handling any sub-codes, like 404.3, that we need handled separately
  • 这允许处理我们需要单独处理的任何子代码,如404.3

  • That works the same in IIS7, IIS Express, and in Casini
  • 在IIS7,IIS Express和Casini中也是如此

  • That works in MVC5, but will continue to work in MVC6, MVC7, and whatever.
  • 这适用于MVC5,但将继续在MVC6,MVC7等工作。

  • That doesn't involve separate code being added to each and every page
  • 这不涉及将单独的代码添加到每个页面

And, I want this to be the canonical answer. Not just "this is what I do", but "this is what Microsoft's developers intended and expected to be done".

并且,我希望这是规范的答案。不只是“这就是我的工作”,而是“这是微软开发人员打算并期望完成的工作”。

Any ideas?

4 个解决方案

#1


4  

I'm not sure if this is the best way but it works for me and its seo friendly: web.config

我不确定这是否是最好的方式,但它对我和它的seo友好:web.config

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

you can define sub status codes too:

您也可以定义子状态代码:

<error statusCode="404" subStatusCode="2" responseMode="ExecuteURL" path="/Error/NotFound" />

and this is how my error controller looks like:

这就是我的错误控制器的样子:

public class ErrorController : Controller
{
    //
    // GET: /Error/      

    public ActionResult NotFound()
    {
        // you have to set your error codes manually
        Response.StatusCode = (int)HttpStatusCode.NotFound;

        return View();
    }
}

#2


4  

The following solution meets most of your requirements except:

以下解决方案满足您的大多数要求,除了:

  • But that don't provide access to anything the user should be required to be authenticated to access This is very dependent on your architecture of your application, however this method does not add any risk of this happening
  • 但是这不能提供对用户应该被要求进行身份验证以访问的任何内容的访问权限这非常依赖于您的应用程序的体系结构,但是这种方法不会增加任何发生这种情况的风险

  • That works the same in IIS7, IIS Express, and in Casini not used Casini so can't guarantee this
  • 在IIS7,IIS Express和Casini没有使用Casini的情况下也是如此,所以无法保证这一点

  • That works in MVC5, but will continue to work in MVC6, MVC7, and whatever. Haven't tried this in MVC6
  • 这适用于MVC5,但将继续在MVC6,MVC7等工作。在MVC6中没试过这个

  • That allows for handling any sub-codes, like 404.3, that we need handled separately It depends what you mean by this - you can return a Response.SubStatusCode to the client in your action results. If you mean to handle it within the application, then provided it is in the exception object thrown, you can access it.
  • 这允许处理我们需要单独处理的任何子代码,如404.3。这取决于你的意思 - 你可以在你的动作结果中将Response.SubStatusCode返回给客户端。如果您要在应用程序中处理它,那么只要它在抛出的异常对象中,您就可以访问它。

Create an ErrorController - this allows you to tailor your end-user error pages and status codes.:

创建一个ErrorController - 这允许您定制最终用户错误页面和状态代码:

[AllowAnonymous]
public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = 500;
        return View();
    }

    public ActionResult UnauthorisedRequest()
    {
        Response.StatusCode = 403;
        return View();
    }

    //Any other errors you want to specifically handle here.

    public ActionResult CatchAllUrls()
    {
        //throwing an exception here pushes the error through the Application_Error method for centralised handling/logging
        throw new HttpException(404, "The requested url " + Request.Url.ToString() + " was not found");
    }
}

Add a route to catch all urls to the end of your route config - this captures all 404's that are not already caught by matching existing routes:

添加路由以捕获路由配置末尾的所有URL - 这将捕获匹配现有路由尚未捕获的所有404:

routes.MapRoute("CatchAllUrls", "{*url}", new { controller = "Error", action = "CatchAllUrls" });

In your global.asax:

在你的global.asax中:

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

        //Error logging omitted

        HttpException httpException = exception as HttpException;
        RouteData routeData = new RouteData();
        IController errorController = new Controllers.ErrorController();
        routeData.Values.Add("controller", "Error");
        routeData.Values.Add("area", "");
        routeData.Values.Add("ex", exception);

        if (httpException != null)
        {
            //this is a basic exampe of how you can choose to handle your errors based on http status codes.
            switch (httpException.GetHttpCode())
            {
                case 404:
                    Response.Clear();

                    // page not found
                    routeData.Values.Add("action", "PageNotFound");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

                    break;
                case 500:
                    // server error
                    routeData.Values.Add("action", "ServerError");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                    break;
                 case 403:
                    // server error
                    routeData.Values.Add("action", "UnauthorisedRequest");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                    break;
                 //add cases for other http errors you want to handle, otherwise HTTP500 will be returned as the default.
                default:
                    // server error
                    routeData.Values.Add("action", "ServerError");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                    break;
            }
        }
        //All other exceptions should result in a 500 error as they are issues with unhandled exceptions in the code
        else
        {
            routeData.Values.Add("action", "ServerError");
            Server.ClearError();
            // Call the controller with the route
            errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
        }
    }

In Summary:

  • Custom pages for the standard HTTP errors You have full control of the page returned to the user
  • 标准HTTP错误的自定义页面您可以完全控制返回给用户的页面

  • That have the same look-and-feel as the standard pages of my site You can use your existign layout if you want to for the custom pages
  • 与我网站的标准页面具有相同的外观您可以使用您的existign布局,如果您想要自定义页面

  • Without having to completely redo everything I have in my Shared/_Layout.cspx as above
  • 无需完全重做我在Shared / _Layout.cspx中的所有内容,如上所述

  • That work whether the user is authenticated or not This is partly down to your architecture but there is nothing in the proposed method that will interfere with user authentication
  • 无论用户是否经过身份验证,这都有效。这部分取决于您的体系结构,但提议的方法中没有任何内容会干扰用户身份验证

  • That should properly return the URL requested with the appropriate HTTP error code, rather than -redirecting to a different page that returns HTTP 200 There is no redirect to the error page so the correct http status code is retured
  • 这应该正确地返回使用适当的HTTP错误代码请求的URL,而不是 - 重定向到返回HTTP的不同页面200没有重定向到错误页面,因此正确的http状态代码被恢复

  • That redirects anything that isn't a valid route to the 404 page, whether it is a missing action, a missing controller, or a missing route
  • 这会将任何不是有效路由的内容重定向到404页面,无论是缺少操作,缺少控制器还是缺少路由

  • That doesn't involve separate code being added to each and every page.
  • 这不涉及将单独的代码添加到每个页面。

#3


2  

There are 2 types of error handling.

有两种类型的错误处理。

  1. Page-level errors
  2. Application-level erros

For page-level errors you can use both web.config or you can create base controller and override OnException event.

对于页面级错误,您既可以使用web.config,也可以创建基本控制器并覆盖OnException事件。

protected override void OnException(ExceptionContext filterContext) { ... }

For application-level errors you can use global.asax Application_Error event

对于应用程序级错误,您可以使用global.asax Application_Error事件

void Application_Error(object sender, EventArgs e) { ... }

Also you can check this link: https://www.simple-talk.com/dotnet/asp.net/handling-errors-effectively-in-asp.net-mvc/

您也可以查看以下链接:https://www.simple-talk.com/dotnet/asp.net/handling-errors-effectively-in-asp.net-mvc/

#4


0  

One Way could be:

单向可能是:

  1. ErrorFilter:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
           HandleErrorAttribute attribute = new HandleErrorAttribute();
           filters.Add(attribute);
    }
    
  2. ErrorFilter:public static void RegisterGlobalFilters(GlobalFilterCollection filters) {        HandleErrorAttribute attribute = new HandleErrorAttribute();        filters.Add(属性); }

  3. Override OnException method of controller

    覆盖控制器的OnException方法

    protected override void OnException(ExceptionContext filterContext)
    {
        EventLog.WriteEntry("YourProjectEventName", filterContext.Exception.ToString(), EventLogEntryType.Error);
        base.OnException(filterContext);
    }
    

Ref link:

http://www.prideparrot.com/blog/archive/2012/5/exception_handling_in_asp_net_mvc

http://www.codeproject.com/Articles/422572/Exception-Handling-in-ASP-NET-MVC

Hope this helps.

希望这可以帮助。

#1


4  

I'm not sure if this is the best way but it works for me and its seo friendly: web.config

我不确定这是否是最好的方式,但它对我和它的seo友好:web.config

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

you can define sub status codes too:

您也可以定义子状态代码:

<error statusCode="404" subStatusCode="2" responseMode="ExecuteURL" path="/Error/NotFound" />

and this is how my error controller looks like:

这就是我的错误控制器的样子:

public class ErrorController : Controller
{
    //
    // GET: /Error/      

    public ActionResult NotFound()
    {
        // you have to set your error codes manually
        Response.StatusCode = (int)HttpStatusCode.NotFound;

        return View();
    }
}

#2


4  

The following solution meets most of your requirements except:

以下解决方案满足您的大多数要求,除了:

  • But that don't provide access to anything the user should be required to be authenticated to access This is very dependent on your architecture of your application, however this method does not add any risk of this happening
  • 但是这不能提供对用户应该被要求进行身份验证以访问的任何内容的访问权限这非常依赖于您的应用程序的体系结构,但是这种方法不会增加任何发生这种情况的风险

  • That works the same in IIS7, IIS Express, and in Casini not used Casini so can't guarantee this
  • 在IIS7,IIS Express和Casini没有使用Casini的情况下也是如此,所以无法保证这一点

  • That works in MVC5, but will continue to work in MVC6, MVC7, and whatever. Haven't tried this in MVC6
  • 这适用于MVC5,但将继续在MVC6,MVC7等工作。在MVC6中没试过这个

  • That allows for handling any sub-codes, like 404.3, that we need handled separately It depends what you mean by this - you can return a Response.SubStatusCode to the client in your action results. If you mean to handle it within the application, then provided it is in the exception object thrown, you can access it.
  • 这允许处理我们需要单独处理的任何子代码,如404.3。这取决于你的意思 - 你可以在你的动作结果中将Response.SubStatusCode返回给客户端。如果您要在应用程序中处理它,那么只要它在抛出的异常对象中,您就可以访问它。

Create an ErrorController - this allows you to tailor your end-user error pages and status codes.:

创建一个ErrorController - 这允许您定制最终用户错误页面和状态代码:

[AllowAnonymous]
public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = 500;
        return View();
    }

    public ActionResult UnauthorisedRequest()
    {
        Response.StatusCode = 403;
        return View();
    }

    //Any other errors you want to specifically handle here.

    public ActionResult CatchAllUrls()
    {
        //throwing an exception here pushes the error through the Application_Error method for centralised handling/logging
        throw new HttpException(404, "The requested url " + Request.Url.ToString() + " was not found");
    }
}

Add a route to catch all urls to the end of your route config - this captures all 404's that are not already caught by matching existing routes:

添加路由以捕获路由配置末尾的所有URL - 这将捕获匹配现有路由尚未捕获的所有404:

routes.MapRoute("CatchAllUrls", "{*url}", new { controller = "Error", action = "CatchAllUrls" });

In your global.asax:

在你的global.asax中:

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

        //Error logging omitted

        HttpException httpException = exception as HttpException;
        RouteData routeData = new RouteData();
        IController errorController = new Controllers.ErrorController();
        routeData.Values.Add("controller", "Error");
        routeData.Values.Add("area", "");
        routeData.Values.Add("ex", exception);

        if (httpException != null)
        {
            //this is a basic exampe of how you can choose to handle your errors based on http status codes.
            switch (httpException.GetHttpCode())
            {
                case 404:
                    Response.Clear();

                    // page not found
                    routeData.Values.Add("action", "PageNotFound");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

                    break;
                case 500:
                    // server error
                    routeData.Values.Add("action", "ServerError");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                    break;
                 case 403:
                    // server error
                    routeData.Values.Add("action", "UnauthorisedRequest");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                    break;
                 //add cases for other http errors you want to handle, otherwise HTTP500 will be returned as the default.
                default:
                    // server error
                    routeData.Values.Add("action", "ServerError");

                    Server.ClearError();
                    // Call the controller with the route
                    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
                    break;
            }
        }
        //All other exceptions should result in a 500 error as they are issues with unhandled exceptions in the code
        else
        {
            routeData.Values.Add("action", "ServerError");
            Server.ClearError();
            // Call the controller with the route
            errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
        }
    }

In Summary:

  • Custom pages for the standard HTTP errors You have full control of the page returned to the user
  • 标准HTTP错误的自定义页面您可以完全控制返回给用户的页面

  • That have the same look-and-feel as the standard pages of my site You can use your existign layout if you want to for the custom pages
  • 与我网站的标准页面具有相同的外观您可以使用您的existign布局,如果您想要自定义页面

  • Without having to completely redo everything I have in my Shared/_Layout.cspx as above
  • 无需完全重做我在Shared / _Layout.cspx中的所有内容,如上所述

  • That work whether the user is authenticated or not This is partly down to your architecture but there is nothing in the proposed method that will interfere with user authentication
  • 无论用户是否经过身份验证,这都有效。这部分取决于您的体系结构,但提议的方法中没有任何内容会干扰用户身份验证

  • That should properly return the URL requested with the appropriate HTTP error code, rather than -redirecting to a different page that returns HTTP 200 There is no redirect to the error page so the correct http status code is retured
  • 这应该正确地返回使用适当的HTTP错误代码请求的URL,而不是 - 重定向到返回HTTP的不同页面200没有重定向到错误页面,因此正确的http状态代码被恢复

  • That redirects anything that isn't a valid route to the 404 page, whether it is a missing action, a missing controller, or a missing route
  • 这会将任何不是有效路由的内容重定向到404页面,无论是缺少操作,缺少控制器还是缺少路由

  • That doesn't involve separate code being added to each and every page.
  • 这不涉及将单独的代码添加到每个页面。

#3


2  

There are 2 types of error handling.

有两种类型的错误处理。

  1. Page-level errors
  2. Application-level erros

For page-level errors you can use both web.config or you can create base controller and override OnException event.

对于页面级错误,您既可以使用web.config,也可以创建基本控制器并覆盖OnException事件。

protected override void OnException(ExceptionContext filterContext) { ... }

For application-level errors you can use global.asax Application_Error event

对于应用程序级错误,您可以使用global.asax Application_Error事件

void Application_Error(object sender, EventArgs e) { ... }

Also you can check this link: https://www.simple-talk.com/dotnet/asp.net/handling-errors-effectively-in-asp.net-mvc/

您也可以查看以下链接:https://www.simple-talk.com/dotnet/asp.net/handling-errors-effectively-in-asp.net-mvc/

#4


0  

One Way could be:

单向可能是:

  1. ErrorFilter:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
           HandleErrorAttribute attribute = new HandleErrorAttribute();
           filters.Add(attribute);
    }
    
  2. ErrorFilter:public static void RegisterGlobalFilters(GlobalFilterCollection filters) {        HandleErrorAttribute attribute = new HandleErrorAttribute();        filters.Add(属性); }

  3. Override OnException method of controller

    覆盖控制器的OnException方法

    protected override void OnException(ExceptionContext filterContext)
    {
        EventLog.WriteEntry("YourProjectEventName", filterContext.Exception.ToString(), EventLogEntryType.Error);
        base.OnException(filterContext);
    }
    

Ref link:

http://www.prideparrot.com/blog/archive/2012/5/exception_handling_in_asp_net_mvc

http://www.codeproject.com/Articles/422572/Exception-Handling-in-ASP-NET-MVC

Hope this helps.

希望这可以帮助。