C# ASP.MVC HandleError属性不为';行不通

C# ASP.MVC HandleError属性不为';行不通,c#,.net,asp.net-mvc,asp.net-mvc-4,C#,.net,Asp.net Mvc,Asp.net Mvc 4,我知道这是一个常见的问题,但我已经抓取了很多讨论,没有结果 我正在尝试使用HandleError ASP.MVC属性处理错误。我使用的是MVC4 我的错误页面位于Views/Shared/Error.cshtml中,如下所示: Test error page <hgroup class="title"> <h1 class="error">Error.</h1> <h2 class="error">An error occurred

我知道这是一个常见的问题,但我已经抓取了很多讨论,没有结果

我正在尝试使用HandleError ASP.MVC属性处理错误。我使用的是MVC4

我的错误页面位于Views/Shared/Error.cshtml中,如下所示:

Test error page
<hgroup class="title">
    <h1 class="error">Error.</h1>
    <h2 class="error">An error occurred while processing your request.</h2>
</hgroup>
我的控制器:

public class TestController : Controller
    {
        [HandleError(View = "Error")]
        public ActionResult Index()
        {
            throw new Exception("oops");
        }
    }
最后,中的my Web.config具有以下节点:

<customErrors mode="On" defaultRedirect="Error">
</customErrors>

当我调用控制器操作时,我会看到一个带有以下文本的白色屏幕:

“/”应用程序中出现服务器错误。 运行时错误描述:处理时发生异常 你的要求。此外,执行时发生另一个异常 第一个异常的自定义错误页。请求已被取消 终止

如果Web.config中未设置defaultRedirect=“Error”,则会出现带有以下文本的黄色屏幕:

“/”应用程序中出现服务器错误。 运行时错误描述:服务器上发生应用程序错误 服务器。此应用程序的当前自定义错误设置阻止 无法查看应用程序错误的详细信息

详细信息:允许显示此特定错误消息的详细信息 可在本地服务器计算机上查看,请创建 位于根目录中的“web.config”配置文件中的标记 当前web应用程序的目录。这个标签 然后应将其“mode”属性设置为“RemoteOnly”。使能 要在远程计算机上查看详细信息,请将“模式”设置为 “关”

注意:您看到的当前错误页面可以替换为 通过修改 应用程序的配置标记指向自定义 错误页面URL

有人知道会出什么问题吗

编辑:

错误是由使用强类型布局引起的。当抛出错误时,MVC的错误处理机制将创建HandleErrorInfo对象,该对象将传递给错误视图。但是,如果我们使用强类型布局,则类型不匹配


在我的案例中,解决方案是在Global.asax中使用Application_Error方法,这在下面的SBirthare中得到了完美的描述。

customErrors
设置为
打开时应该足以在本地查看结果

<customErrors mode="On" />
只要您已在
filterConfig
中注册了
HandleErrorAttribute
,并且

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
Global.asax.cs
中的
Application\u Start()
中,那么它应该可以工作

如果你要创建自定义错误页面,我建议你阅读这篇博文


多年来,我一直努力在ASP.NET MVC中顺利实现“处理自定义错误”

我以前曾成功使用过Elmah,但由于大量需要以不同方式处理和测试的案例(即本地与IIS)而不知所措

最近,在我的一个项目中,我使用了以下方法(似乎在本地和生产环境中工作良好)

我根本没有在web.config中指定
customErrors
或任何设置

我覆盖
Application\u Error
并在那里处理所有情况,调用
ErrorController
中的特定操作

如果有帮助的话,我会分享这篇文章,并获得反馈(虽然事情正在进行,但你永远不知道它什么时候开始破裂;)

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
    }

    protected void Application_Error(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine("Enter - Application_Error");

        var httpContext = ((MvcApplication)sender).Context;

        var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
        var currentController = " ";
        var currentAction = " ";

        if (currentRouteData != null)
        {
            if (currentRouteData.Values["controller"] != null &&
                !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
            {
                currentController = currentRouteData.Values["controller"].ToString();
            }

            if (currentRouteData.Values["action"] != null &&
                !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
            {
                currentAction = currentRouteData.Values["action"].ToString();
            }
        }

        var ex = Server.GetLastError();

        if (ex != null)
        {
            System.Diagnostics.Trace.WriteLine(ex.Message);

            if (ex.InnerException != null)
            {
                System.Diagnostics.Trace.WriteLine(ex.InnerException);
                System.Diagnostics.Trace.WriteLine(ex.InnerException.Message);
            }
        }

        var controller = new ErrorController();
        var routeData = new RouteData();
        var action = "CustomError";
        var statusCode = 500;

        if (ex is HttpException)
        {
            var httpEx = ex as HttpException;
            statusCode = httpEx.GetHttpCode();

            switch (httpEx.GetHttpCode())
            {
                case 400:
                    action = "BadRequest";
                    break;

                case 401:
                    action = "Unauthorized";
                    break;

                case 403:
                    action = "Forbidden";
                    break;

                case 404:
                    action = "PageNotFound";
                    break;

                case 500:
                    action = "CustomError";
                    break;

                default:
                    action = "CustomError";
                    break;
            }
        }
        else if (ex is AuthenticationException)
        {
            action = "Forbidden";
            statusCode = 403;
        }

        httpContext.ClearError();
        httpContext.Response.Clear();
        httpContext.Response.StatusCode = statusCode;
        httpContext.Response.TrySkipIisCustomErrors = true;
        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = action;

        controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
    }

}
public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult CustomError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return View();
    }
}
ErrorController.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterAuth();
    }

    protected void Application_Error(object sender, EventArgs e)
    {
        System.Diagnostics.Trace.WriteLine("Enter - Application_Error");

        var httpContext = ((MvcApplication)sender).Context;

        var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
        var currentController = " ";
        var currentAction = " ";

        if (currentRouteData != null)
        {
            if (currentRouteData.Values["controller"] != null &&
                !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
            {
                currentController = currentRouteData.Values["controller"].ToString();
            }

            if (currentRouteData.Values["action"] != null &&
                !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
            {
                currentAction = currentRouteData.Values["action"].ToString();
            }
        }

        var ex = Server.GetLastError();

        if (ex != null)
        {
            System.Diagnostics.Trace.WriteLine(ex.Message);

            if (ex.InnerException != null)
            {
                System.Diagnostics.Trace.WriteLine(ex.InnerException);
                System.Diagnostics.Trace.WriteLine(ex.InnerException.Message);
            }
        }

        var controller = new ErrorController();
        var routeData = new RouteData();
        var action = "CustomError";
        var statusCode = 500;

        if (ex is HttpException)
        {
            var httpEx = ex as HttpException;
            statusCode = httpEx.GetHttpCode();

            switch (httpEx.GetHttpCode())
            {
                case 400:
                    action = "BadRequest";
                    break;

                case 401:
                    action = "Unauthorized";
                    break;

                case 403:
                    action = "Forbidden";
                    break;

                case 404:
                    action = "PageNotFound";
                    break;

                case 500:
                    action = "CustomError";
                    break;

                default:
                    action = "CustomError";
                    break;
            }
        }
        else if (ex is AuthenticationException)
        {
            action = "Forbidden";
            statusCode = 403;
        }

        httpContext.ClearError();
        httpContext.Response.Clear();
        httpContext.Response.StatusCode = statusCode;
        httpContext.Response.TrySkipIisCustomErrors = true;
        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = action;

        controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
    }

}
public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult CustomError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return View();
    }
}
这是我所有的。未注册任何
HandleErrorAttribute


我发现这种方法不那么令人困惑,而且易于扩展。希望这对某人有所帮助。

我不知道HandleErrorAttribute到底做了什么。它似乎一点作用也没有

无论如何,OneException()中只需要4行代码就可以使其表现出我预期的效果:

    // Copyright(c) 2016 Google Inc.
    //
    // Licensed under the Apache License, Version 2.0 (the "License"); you may not
    // use this file except in compliance with the License. You may obtain a copy of
    // the License at
    //
    // http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    // License for the specific language governing permissions and limitations under
    // the License.

    using System.Web.Mvc;

    namespace GoogleCloudSamples
    {
        internal class CustomHandleErrorAttribute : HandleErrorAttribute
        {
            public override void OnException(ExceptionContext filterContext)
            {
                // Why oh Why doesn't base.OnException(filterContext) do this?
                ViewDataDictionary viewData = new ViewDataDictionary(filterContext);
                filterContext.Result = new ViewResult() { ViewName = "Error", ViewData = viewData };
                filterContext.HttpContext.Response.StatusCode = 500;
                filterContext.ExceptionHandled = true;
            }
        }

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

我在几乎所有的应用程序中都使用DI。即使您不使用依赖注入,它对于MVC(Web API)应用程序的全局异常处理程序也是非常有用的

我喜欢@SBirthare的方法——但我会把它放在任何国际奥委会都会解决的课程中

我更喜欢Autofac—但是将@SBirthare的技术与一些DI结合起来,应该可以为您提供一个集中的位置来配置异常处理—而且还可以注册不同类型的异常处理(如果您需要的话)

这是我的传统做法:

public abstract class ExceptionHandlerService : IExceptionHandlerService
{
    ILoggingService _loggingSerivce;

    protected ExceptionHandlerService(ILoggingService loggingService)
    {
        //Doing this allows my IoC component to resolve whatever I have
        //configured to log "stuff"
        _loggingService = loggingService;


    }
    public virtual void HandleException(Exception exception)
    {


        //I use elmah a alot - and this can handle WebAPI 
       //or Task.Factory ()=> things where the context is null
        if (Elmah.ErrorSignal.FromCurrentContext() != null)
        {
            Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
        }
        else
        {
            ErrorLog.GetDefault(null).Log(new Error(exception));
        }

        _loggingService.Log("something happened", exception)
    }
}
现在你需要注册这个

builder.RegisterType<ExceptionHandlerService>().As<IExceptionHandlerService();
在Autofac中注册过滤器

builder.Register(ctx => new CustomHandleError(ctx.Resolve<IExceptionHandlerService>())).AsExceptionFilterFor<BaseController>();
}

这实现了同样的效果——但是现在您使用依赖项注入,并且能够注册多个ExceptionHandler并基于异常类型解析服务

解决方案:

首先,删除web.config文件中的“defaultRedirect”属性

其次,在您的FilterConfig.cs文件中,我发现有些人引用了一些MVC模板中的“HandleErrorAttribute”的“自定义类”版本,有些开发人员已经为其他人在线创建了该版本。这些是从默认错误视图页面断开原始MVC HandlerErrorAttribute类的连接

您可以通过如下“使用”语句确保引用FilterConfig文件中的原始Microsoft MVC HandleErrorAttribute来修复此问题,并向其添加全局“错误”视图,以确保现在再次调用该页面。见下文

using System.Web.Mvc;//add this to make sure you are referencing the MVC version
public class FilterConfig
{
    public static void Configure(System.Web.Mvc.GlobalFilterCollection filters)
    {
        // Note I added {View = "Error"}. This applies the Error View Page to all Actions in all Controller classes
        filters.Add(new HandleErrorAttribute { View = "Error" });
    }
}

这将全局地将共享视图文件夹中的“Error.cshtml”视图分配给引发的每个异常,当然404和其他服务器错误除外。您可以通过上面开发人员概述的其他方式处理这些问题。但这会将.NET异常路由到自定义错误页。-斯托克利

谢谢您的回复。现在我的Web.config是:并且我的TestController.cs是:公共类TestController:Controller{public ActionResult Index(){throw new Exception(“oops”);}但是我仍然得到了第二个描述的黄色屏幕,
public abstract class ExceptionHandlerService : IExceptionHandlerService
{
ILoggingService _loggingSerivce;

protected ExceptionHandlerService(ILoggingService loggingService)
{
    //Doing this allows my IoC component to resolve whatever I have
    //configured to log "stuff"
    _loggingService = loggingService;


}
public virtual void HandleException(Exception exception)
{


    //I use elmah a alot - and this can handle WebAPI 
   //or Task.Factory ()=> things where the context is null
    if (Elmah.ErrorSignal.FromCurrentContext() != null)
    {
        Elmah.ErrorSignal.FromCurrentContext().Raise(exception);
    }
    else
    {
        ErrorLog.GetDefault(null).Log(new Error(exception));
    }

    _loggingService.Log("something happened", exception)

    //re-direct appropriately
    var controller = new ErrorController();
    var routeData = new RouteData();
    var action = "CustomError";
    var statusCode = 500;

        statusCode = exception.GetHttpCode();

        switch (exception.GetHttpCode())
        {
            case 400:
                action = "BadRequest";
                break;

            case 401:
                action = "Unauthorized";
                break;

            case 403:
                action = "Forbidden";
                break;

            case 404:
                action = "PageNotFound";
                break;

            case 500:
                action = "CustomError";
                break;

            default:
                action = "CustomError";
                break;
         }
    //I didn't add the Authentication Error because that should be a separate filter that Autofac resolves.

   var httpContext = ((MvcApplication)sender).Context;
    httpContext.ClearError();
    httpContext.Response.Clear();
    httpContext.Response.StatusCode = statusCode;
    httpContext.Response.TrySkipIisCustomErrors = true;
    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = action;

    controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
    ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}
using System.Web.Mvc;//add this to make sure you are referencing the MVC version
public class FilterConfig
{
    public static void Configure(System.Web.Mvc.GlobalFilterCollection filters)
    {
        // Note I added {View = "Error"}. This applies the Error View Page to all Actions in all Controller classes
        filters.Add(new HandleErrorAttribute { View = "Error" });
    }
}