簡體   English   中英

ASP.MVC HandleError屬性不起作用

[英]ASP.MVC HandleError attribute doesn't work

我知道這是一個常見的問題,但我已經抓住了許多討論而沒有結果。

我正在嘗試使用HandleError ASP.MVC attrbiute來處理錯誤。 我正在使用MVC 4。

我的錯誤頁面位於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>

App-Start文件夾中的My FilterConfig.cs是:

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

我的控制器:

public class TestController : Controller
    {
        [HandleError(View = "Error")]
        public ActionResult Index()
        {
            throw new Exception("oops");
        }
    }

最后我的Web.config in有以下節點:

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

當我調用控制器動作時,我得到一個帶有以下文本的白色屏幕:

'/'應用程序中的服務器錯誤。

運行時錯誤說明:處理請求時發生異常。 此外,執行第一個異常的自定義錯誤頁面時發生另一個異常。 請求已終止。

如果未在Web.config中設置defaultRedirect =“Error”,則會出現以下文本的黃色屏幕:

'/'應用程序中的服務器錯誤。

運行時錯誤說明:服務器上發生應用程序錯誤。 此應用程序的當前自定義錯誤設置可防止查看應用程序錯誤的詳細信息。

詳細信息:要在本地服務器計算機上查看此特定錯誤消息的詳細信息,請在位於當前Web應用程序根目錄中的“web.config”配置文件中創建標記。 然后,此標記應將其“mode”屬性設置為“RemoteOnly”。 要使詳細信息可在遠程計算機上查看,請將“mode”設置為“Off”。

注意:通過修改應用程序配置標記的“defaultRedirect”屬性以指向自定義錯誤頁面URL,可以將自動查看的當前錯誤頁面替換為自定義錯誤頁面。

有人知道什么是錯的嗎?

編輯:

錯誤是由使用強類型布局引起的。 拋出錯誤時,MVC的錯誤處理機制正在創建HandleErrorInfo對象,該對象將傳遞給Error視圖。 但是,如果我們使用強類型布局,則類型不匹配。

我的解決方案是使用Global.asax中的Application_Error方法,下面的SBirthare對此進行了完美描述。

多年來,我一直在努力順利地在ASP.NET MVC中實現“處理自定義錯誤”。

我之前已經成功地使用過Elmah,但卻被大量需要處理和測試的案例所淹沒(即本地與IIS)。

最近在我的一個現在正在進行的項目中,我使用了以下方法(似乎在本地和生產環境中工作正常)。

我根本沒有在web.config中指定customErrors或任何設置。

我重寫Application_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));
    }

}

ErrorController.cs

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

我發現這種方法不那么容易混淆和擴展。 希望這有助於某人。

customErrors設置為on應足以在本地查看結果。

<customErrors mode="On" />

當您在全局注冊HandleErrorAttribute ,您不需要使用它來修飾您的action方法,因為它將默認應用。

public class TestController : Controller
{
    public ActionResult Index()
    {
        throw new Exception("oops");
        return View();
    }
}

只要您在HandleErrorAttribute中注冊了HandleErrorAttribute filterConfig

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

Global.asax.cs中的Application_Start()中它應該可以工作。

如果您要創建自定義錯誤頁面,我建議您閱讀此博客文章

Mvc自定義錯誤頁面

我在幾乎所有的應用程序中使用DI。 即使您不使用依賴注入 - 它對於MVC(Web API)應用程序的全局異常處理程序也非常有用。

我喜歡@ SBirthare的方法 - 但我會把它放在任何IoC都能解決的類中。

我更喜歡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();

在MVC應用程序中 - 您需要實現一個實現IExceptionFilter的類

public class CustomHandleError : IExceptionFilter
{
    private readonly IExceptionHandlerService _exceptionHandlerService;
    public CustomHandleError(IExceptionHandlerService exceptionHandlerService)
    {
        _exceptionHandlerService = exceptionHandlerService;
    }

    public void OnException(ExceptionContext filterContext)
    {
        _exceptionHandlerService.HandleException(filterContext.Exception);
    }
}

在Autofac中注冊過濾器

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

我總是定義一個我所有其他控制器派生自的BaseController。 您可以使用相同的技術定義授權過濾器。 現在所有控制器都是安全的並且處理異常

現在你不需要任何類的屬性 - 代碼在一個地方。

我沒有任何嘗試catch的任何地方,所以我們可以在異常處理程序捕獲異常時保留堆棧跟蹤。

如果你將這種技術與@ SBirthare結合使用 -

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));
}

}

這實現了同樣的目的 - 但現在您使用依賴注入,您可以注冊多個ExceptionHandler並根據異常類型解析服務。

我無法弄清楚HandleErrorAttribute實際上做了什么。 它似乎什么都不做。

無論如何,它只需要在OnException()中使用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());
            }
        }
    }

解:

首先,刪除web.config文件中的“defaultRedirect”屬性。

其次,在你的FilterConfig.cs文件中,我發現有些人正在引用一些MVC模板中的“HandleErrorAttribute”的“自定義類”版本,一些開發人員已經在線創建了其他版本。 這些將斷開ORIGINAL MVC HandlerErrorAttribute類與默認的錯誤視圖頁面。

您可以通過確保使用“using”語句在FilterConfig文件中引用ORIGINAL 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異常路由到您的自定義錯誤頁面。 - 斯托克利

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM