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

时间:2014-08-26 22:28:37

标签: asp.net-mvc

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

他们彼此之间并不一致,而且他们中的大多数似乎都不起作用。

我想要的是什么:

  • 标准HTTP错误的自定义页面
  • 与我网站的标准页面具有相同的外观和感觉
  • 无需完全重做我在Shared / _Layout.cspx
  • 中的所有内容
  • 无论用户是否经过身份验证,都能正常工作
  • 但是,这不提供访问
  • 需要用户进行身份验证的任何内容的访问权限
  • 应正确返回使用相应HTTP错误代码请求的URL,而不是重定向到返回HTTP 200的其他页面
  • 将任何不是有效路线的内容重定向到404页面,无论是缺少操作,缺少控制器还是缺少路径
  • 允许处理我们需要单独处理的任何子代码,如404.3
  • 在IIS7,IIS Express和Casini
  • 中的工作方式相同
  • 适用于MVC5,但将继续在MVC6,MVC7等中运行。
  • 这不涉及将单独的代码添加到每个页面

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

有什么想法吗?

4 个答案:

答案 0 :(得分:4)

我不确定这是否是最好的方式,但它适用于我及其友好的seo: web.config

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

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

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

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

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

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

        return View();
    }
}

答案 1 :(得分:4)

以下解决方案满足您的大部分要求,但:

  • 但是,这不能提供对用户进行身份验证以访问的任何内容的访问这非常依赖于您的应用程序的体系结构,但是这种方法不会增加任何发生这种情况的风险
  • 在IIS7,IIS Express和Casini中的工作方式相同未使用Casini,因此无法保证此
  • 这适用于MVC5,但将继续在MVC6,MVC7等中运行。 在MVC6中没试过这个
  • 允许处理我们需要单独处理的任何子代码,如404.3 这取决于你的意思 - 你可以在你的动作结果中将Response.SubStatusCode返回给客户端。如果您打算在应用程序中处理它,那么只要它在抛出的异常对象中,您就可以访问它。

创建一个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");
    }
}

添加一条路线以捕获路线配置末尾的所有网址 - 这会捕获匹配现有路线尚未捕获的所有404:

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

在你的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));
        }
    }

摘要:

  • 标准HTTP错误的自定义页面您可以完全控制返回给用户的页面
  • 与我网站的标准页面具有相同的外观如果您想要自定义页面,可以使用existign布局
  • 无需完全重做我在Shared / _Layout.cspx 中的所有内容
  • 无论用户是否经过身份验证,都能正常工作这部分取决于您的体系结构,但建议的方法中没有任何内容会干扰用户身份验证
  • 应正确返回使用相应HTTP错误代码请求的URL,而不是 - 重定向到返回HTTP 200的其他页面没有重定向到错误页面,因此正确的http状态代码被撤消
  • 将任何不是有效路线的内容重定向到404页面,无论是缺少操作,缺少控制器还是缺少路径
  • 这不涉及将单独的代码添加到每个页面。

答案 2 :(得分:2)

有两种类型的错误处理。

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

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

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

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

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

答案 3 :(得分:0)

单向可能是:

  1. ErrorFilter:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
           HandleErrorAttribute attribute = new HandleErrorAttribute();
           filters.Add(attribute);
    }
    
  2. 覆盖控制器的OnException方法

    protected override void OnException(ExceptionContext filterContext)
    {
        EventLog.WriteEntry("YourProjectEventName", filterContext.Exception.ToString(), EventLogEntryType.Error);
        base.OnException(filterContext);
    }
    
  3. 参考链接:

    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

    希望这有帮助。