ASP.NET MVC中的错误处理

Adr*_*ila 64 asp.net-mvc exception

如何正确处理ASP.NET MVC中从控制器抛出的异常?该HandleError属性似乎只处理由MVC基础结构抛出的异常,而不是我自己的代码抛出的异常.

使用此web.config

<customErrors mode="On">
    <error statusCode="401" redirect="/Errors/Http401" />
</customErrors>
Run Code Online (Sandbox Code Playgroud)

使用以下代码

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            // Force a 401 exception for testing
            throw new HttpException(401, "Unauthorized");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不会导致我所希望的.相反,我得到通用的ASP.NET错误页面,告诉我修改我的web.config以查看实际的错误信息.但是,如果不是抛出异常而是返回无效的View,我会得到/Shared/Views/Error.aspx页面:

return View("DoesNotExist");
Run Code Online (Sandbox Code Playgroud)

像我上面所做的那样在控制器中抛出异常似乎绕过了所有HandleError功能,那么创建错误页面的正确方法是什么,以及如何在MVC基础架构中发挥良好作用?

小智 63

Controller.OnException(ExceptionContext context).覆盖它.

protected override void OnException(ExceptionContext filterContext)
{
    // Bail if we can't do anything; app will crash.
    if (filterContext == null)
        return;
        // since we're handling this, log to elmah

    var ex = filterContext.Exception ?? new Exception("No further information exists.");
    LogException(ex);

    filterContext.ExceptionHandled = true;
    var data = new ErrorPresentation
        {
            ErrorMessage = HttpUtility.HtmlEncode(ex.Message),
            TheException = ex,
            ShowMessage = !(filterContext.Exception == null),
            ShowLink = false
        };
    filterContext.Result = View("ErrorPage", data);
}
Run Code Online (Sandbox Code Playgroud)

  • 您将需要在application.Error的global.asax文件中使用类似的代码,以确保处理在控制器之外发生的异常(例如,路由异常以使请求与多个路由匹配).它似乎不那么粗略地处理控制器中的异常; 即使它工作,在处理同时请求的服务器上的Server.GetLastError感觉不对.在global.asax中使用类似的代码会提醒您所有异常. (6认同)

Adr*_*ila 21

感谢kazimanzurrashaid,这是我在Global.asax.cs做的事情:

protected void Application_Error()
{
    Exception unhandledException = Server.GetLastError();
    HttpException httpException = unhandledException as HttpException;
    if (httpException == null)
    {
        Exception innerException = unhandledException.InnerException;
        httpException = innerException as HttpException;
    }

    if (httpException != null)
    {
        int httpCode = httpException.GetHttpCode();
        switch (httpCode)
        {
            case (int) HttpStatusCode.Unauthorized:
                Response.Redirect("/Http/Error401");
                break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我将能够根据我需要支持的任何其他HTTP错误代码向HttpContoller添加更多页面.


Cra*_*ntz 13

HandleError属性似乎只处理MVC基础结构抛出的异常,而不是我自己的代码抛出的异常.

那是错的.实际上,HandleError只会"处理"您自己的代码或您自己的代码调用的代码中抛出的异常.换句话说,只有您的操作在调用堆栈中的异常.

您所看到的行为的真实解释是您抛出的特定异常.HandleError与HttpException的行为不同.从源代码:

        // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
        // ignore it.
        if (new HttpException(null, exception).GetHttpCode() != 500) {
            return;
        }
Run Code Online (Sandbox Code Playgroud)

  • 我可以解释发生了什么,但我需要更多信息才能告诉你为你的应用做正确的事情.HandleError适用于某个操作,通常不会在操作中抛出HttpExceptions.我可以解释你所看到的行为背后的事实,而不是猜测你实际上想要做什么并给你可能不正确的建议.如果您想要解释更多关于您真正想做的事情,请随时更新问题. (4认同)

kaz*_*hid 6

我不认为您将能够基于具有HandleError属性的HttpCode显示特定的ErrorPage,我更倾向于使用HttpModule来实现此目的.假设我有文件夹"ErrorPages",其中存在针对每个特定错误的不同页面,并且在web.config中指定映射与常规Web表单应用程序相同.以下是用于显示错误页面的代码:

public class ErrorHandler : BaseHttpModule{

public override void OnError(HttpContextBase context)
{
    Exception e = context.Server.GetLastError().GetBaseException();
    HttpException httpException = e as HttpException;
    int statusCode = (int) HttpStatusCode.InternalServerError;

    // Skip Page Not Found and Service not unavailable from logging
    if (httpException != null)
    {
        statusCode = httpException.GetHttpCode();

        if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable))
        {
            Log.Exception(e);
        }
    }

    string redirectUrl = null;

    if (context.IsCustomErrorEnabled)
    {
        CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors");

        if (section != null)
        {
            redirectUrl = section.DefaultRedirect;

            if (httpException != null)
            {
                if (section.Errors.Count > 0)
                {
                    CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)];

                    if (item != null)
                    {
                        redirectUrl = item.Redirect;
                    }
                }
            }
        }
    }

    context.Response.Clear();
    context.Response.StatusCode = statusCode;
    context.Response.TrySkipIisCustomErrors = true;

    context.ClearError();

    if (!string.IsNullOrEmpty(redirectUrl))
    {
        context.Server.Transfer(redirectUrl);
    }
}
Run Code Online (Sandbox Code Playgroud)

}