ASP.NET MVC HandleError


110

[HandleError]asp.net MVC Preview 5 의 필터는 어떻게하나요?
내 Web.config 파일에 customErrors를 설정했습니다.

<customErrors mode="On" defaultRedirect="Error.aspx">
  <error statusCode="403" redirect="NoAccess.htm"/>
  <error statusCode="404" redirect="FileNotFound.htm"/>
</customErrors>

다음과 같이 내 컨트롤러 클래스 위에 [HandleError]를 넣습니다.

[HandleError]
public class DSWebsiteController: Controller
{
    [snip]
    public ActionResult CrashTest()
    {
        throw new Exception("Oh Noes!");
    }
}

그런 다음 컨트롤러가이 클래스에서 상속하도록하고 CrashTest ()를 호출합니다. Visual Studio가 오류에서 중지되고 계속하려면 f5 키를 누른 후 Error.aspx? aspxerrorpath = / sxi.mvc / CrashTest로 다시 라우팅됩니다 (여기서 sxi는 사용 된 컨트롤러의 이름입니다. 물론 경로를 찾을 수 없습니다. " '/'응용 프로그램에 서버 오류가 있습니다."404.

이 사이트는 미리보기 3에서 5로 포팅되었습니다. 오류 처리를 제외하고 모든 것이 실행됩니다 (포팅 작업이 많지 않았습니다). 완전한 새 프로젝트를 만들 때 오류 처리가 작동하는 것 같습니다.

아이디어?

-참고-
이 질문은 현재 3K 이상의 조회수를 가지고 있기 때문에 현재 사용중인 (ASP.NET MVC 1.0)을 입력하는 것이 도움이 될 것이라고 생각했습니다. 에서 MVC있는 contrib 프로젝트 당신은 아마 너무 그것을 확인해야한다 "RescueAttribute"라는 화려한 속성이있다)


답변:


158
[HandleError]

클래스 (또는 해당 문제에 대한 작업 메서드)에 HandleError 특성 만 제공하면 처리되지 않은 예외가 발생하면 MVC는 먼저 Controller의 View 폴더에서 "Error"라는 해당 뷰를 찾습니다. 찾을 수없는 경우 Shared View 폴더 (기본적으로 Error.aspx 파일이 있어야 함)를 검색합니다.

[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "LameErrorHandling")]

찾고있는 예외 유형에 대한 특정 정보로 추가 속성을 쌓을 수도 있습니다. 이 시점에서 오류를 기본 "오류"보기가 아닌 특정보기로 보낼 수 있습니다.

자세한 내용은 Scott Guthrie의 블로그 게시물 을 참조하십시오.


1
확장 된 정보에 감사드립니다. 내가 뭘 잘못했는지 모르겠지만 새 프로젝트를 만들고 기존의 모든 뷰, 컨트롤러 및 모델을 포팅했고 이제 작동합니다. 그래도 선택적 견해에 대해 몰랐습니다.
Boris Callens

이러한 예외의 로깅이 필요한 경우 뷰에 코드 숨김을 추가 할 수있는 곳이 될까요?
Peter J

6
상징적입니다. 귀하의 의견에 대한 "결코 늦게"회신합니다. 대신 HandleErrorAttribute를 하위 클래스 화하고 "OnException"메서드를 재정의 할 수 있습니다. 그런 다음 원하는 로깅 또는 사용자 지정 작업을 삽입합니다. 그런 다음 예외를 완전히 처리하거나 (context.ExceptionHandled를 true로 설정)이를 위해 기본 클래스의 자체 OnException 메서드로 다시 연기 할 수 있습니다. 여기에 도움이 될 수있는 훌륭한 기사가 있습니다. blog.dantup.me.uk/2009/04/…
Funka

나는이 내부 처리 할 수 있도록 컨트롤러의 여지가 global.asax같은 메시지를 사용자에게 보여?
shaijut

@PartialView와 동일한 오류 페이지를 사용하고 예외가 발생한 후 모달 대화 상자에서 렌더링하는 것은 어떻습니까? 답변에 예를 들어 주시겠습니까? 내가 달성하고 싶은 것은 MVC에서 PartialView를 사용하여 글로벌 오류 처리 에 설명되었습니다 .
Jack

23

또한 http 오류 코드를 500으로 설정하지 않는 오류에 유의해야합니다.

(예 : UnauthorizedAccessException)

HandleError 필터에 의해 처리되지 않습니다.


1
사실이지만 MVC 기여에서 RescueAttribute를 확인하십시오 (OP의 링크)
Boris Callens

14

500에 대한 http 오류 코드에 대한 해결책은 [ERROR]라는 속성으로 조치를 취합니다.

public class Error: System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {

            if (filterContext.HttpContext.IsCustomErrorEnabled)
            {
                filterContext.ExceptionHandled = true;

            }
            base.OnException(filterContext);
            //OVERRIDE THE 500 ERROR  
           filterContext.HttpContext.Response.StatusCode = 200;
    }

    private static void RaiseErrorSignal(Exception e)
    {
        var context = HttpContext.Current;
      // using.Elmah.ErrorSignal.FromContext(context).Raise(e, context);
    } 

}

//예:

[Error]
[HandleError]
[PopulateSiteMap(SiteMapName="Mifel1", ViewDataKey="Mifel1")]
public class ApplicationController : Controller
{
}

12

MVC의 속성은 get 및 post 메소드 에서 오류 처리에 매우 유용 하며 ajax 호출 도 추적합니다 .

애플리케이션에서 기본 컨트롤러를 만들고 기본 컨트롤러 (EmployeeController)에서 상속합니다.

공용 클래스 EmployeeController : BaseController

기본 컨트롤러에 아래 코드를 추가하십시오.

/// <summary>
/// Base Controller
/// </summary>
public class BaseController : Controller
{       
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception ex = filterContext.Exception;

        //Save error log in file
        if (ConfigurationManager.AppSettings["SaveErrorLog"].ToString().Trim().ToUpper() == "TRUE")
        {
            SaveErrorLog(ex, filterContext);
        }

        // if the request is AJAX return JSON else view.
        if (IsAjax(filterContext))
        {
            //Because its a exception raised after ajax invocation
            //Lets return Json
            filterContext.Result = new JsonResult()
            {
                Data = Convert.ToString(filterContext.Exception),
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();

            filterContext.Result = new ViewResult()
            {
                //Error page to load
                ViewName = "Error",
                ViewData = new ViewDataDictionary()
            };

            base.OnException(filterContext);
        }
    }

    /// <summary>
    /// Determines whether the specified filter context is ajax.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    private bool IsAjax(ExceptionContext filterContext)
    {
        return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
    }

    /// <summary>
    /// Saves the error log.
    /// </summary>
    /// <param name="ex">The ex.</param>
    /// <param name="filterContext">The filter context.</param>
    void SaveErrorLog(Exception ex, ExceptionContext filterContext)
    {
        string logMessage = ex.ToString();

        string logDirectory = Server.MapPath(Url.Content("~/ErrorLog/"));

        DateTime currentDateTime = DateTime.Now;
        string currentDateTimeString = currentDateTime.ToString();
        CheckCreateLogDirectory(logDirectory);
        string logLine = BuildLogLine(currentDateTime, logMessage, filterContext);
        logDirectory = (logDirectory + "\\Log_" + LogFileName(DateTime.Now) + ".txt");

        StreamWriter streamWriter = null;
        try
        {
            streamWriter = new StreamWriter(logDirectory, true);
            streamWriter.WriteLine(logLine);
        }
        catch
        {
        }
        finally
        {
            if (streamWriter != null)
            {
                streamWriter.Close();
            }
        }
    }

    /// <summary>
    /// Checks the create log directory.
    /// </summary>
    /// <param name="logPath">The log path.</param>
    bool CheckCreateLogDirectory(string logPath)
    {
        bool loggingDirectoryExists = false;
        DirectoryInfo directoryInfo = new DirectoryInfo(logPath);
        if (directoryInfo.Exists)
        {
            loggingDirectoryExists = true;
        }
        else
        {
            try
            {
                Directory.CreateDirectory(logPath);
                loggingDirectoryExists = true;
            }
            catch
            {
            }
        }

        return loggingDirectoryExists;
    }

    /// <summary>
    /// Builds the log line.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    /// <param name="logMessage">The log message.</param>
    /// <param name="filterContext">The filter context.</param>       
    string BuildLogLine(DateTime currentDateTime, string logMessage, ExceptionContext filterContext)
    {
        string controllerName = filterContext.RouteData.Values["Controller"].ToString();
        string actionName = filterContext.RouteData.Values["Action"].ToString();

        RouteValueDictionary paramList = ((System.Web.Routing.Route)(filterContext.RouteData.Route)).Defaults;
        if (paramList != null)
        {
            paramList.Remove("Controller");
            paramList.Remove("Action");
        }

        StringBuilder loglineStringBuilder = new StringBuilder();

        loglineStringBuilder.Append("Log Time : ");
        loglineStringBuilder.Append(LogFileEntryDateTime(currentDateTime));
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("Username : ");
        loglineStringBuilder.Append(Session["LogedInUserName"]);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ControllerName : ");
        loglineStringBuilder.Append(controllerName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ActionName : ");
        loglineStringBuilder.Append(actionName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("----------------------------------------------------------------------------------------------------------");
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append(logMessage);
        loglineStringBuilder.Append(System.Environment.NewLine);
        loglineStringBuilder.Append("==========================================================================================================");

        return loglineStringBuilder.ToString();
    }

    /// <summary>
    /// Logs the file entry date time.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileEntryDateTime(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd-MMM-yyyy HH:mm:ss");
    }

    /// <summary>
    /// Logs the name of the file.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileName(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd_MMM_yyyy");
    }

}

===============================================

디렉터리를 찾습니다 : Root / App_Start / FilterConfig.cs

아래 코드를 추가하십시오.

/// <summary>
/// Filter Config
/// </summary>
public class FilterConfig
{
    /// <summary>
    /// Registers the global filters.
    /// </summary>
    /// <param name="filters">The filters.</param>
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

AJAX 오류 추적 :

레이아웃 페이지로드에서 CheckAJAXError 함수를 호출합니다.

function CheckAJAXError() {
    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {

        var ex;
        if (String(thrownError).toUpperCase() == "LOGIN") {
            var url = '@Url.Action("Login", "Login")';
            window.location = url;
        }
        else if (String(jqXHR.responseText).toUpperCase().indexOf("THE DELETE STATEMENT CONFLICTED WITH THE REFERENCE CONSTRAINT") >= 0) {

            toastr.error('ReferanceExistMessage');
        }
        else if (String(thrownError).toUpperCase() == "INTERNAL SERVER ERROR") {
            ex = ajaxSettings.url;
            //var url = '@Url.Action("ErrorLog", "Home")?exurl=' + ex;
            var url = '@Url.Action("ErrorLog", "Home")';
            window.location = url;
        }
    });
};

AJAX 요청에 대한 예외 세부 정보를 유출하고 있지만 항상 원하는 것은 아닙니다. 로깅 코드는 스레드로부터 안전하지 않습니다. 오류 뷰가 있다고 가정하고 응답 코드를 변경하지 않고 반환합니다. 그런 다음 JavaScript에서 특정 오류 문자열을 확인합니다 (현지화는 어떻습니까?). 기본적으로 기존 답변이 이미 " OnException예외를 처리하기 위해 재정의" 라고 말한 것을 반복하고 있지만 꽤 나쁜 구현을 보여줍니다.
CodeCaster

@ School.Resource.Messages.ReferanceExist 매개 변수는 무엇입니까?
Jack

@CodeCaster ASP.NET MVC에서 AJAX와 함께 이러한 종류의 오류 처리 방법을 사용하는 더 좋은 방법을 알고 있습니까? 도와주세요.
Jack

HTTP가 의도 한대로 400 또는 500을 반환합니다. 응답 본문에서 특정 문자열을 파헤 치지 마십시오.
CodeCaster

@CodeCaster 이 문제와 관련하여 MVC에서 PartialView를 사용한 전역 오류 처리를 살펴볼 수 있습니까?
Jack

4

Error.aspx가 없습니다. :) 미리보기 5에서는 Views / Shared 폴더에 있습니다. 새 Preview 5 프로젝트에서 복사하기 만하면됩니다.


회신 해 주셔서 감사합니다.하지만 이미 Error.aspx 페이지를 복사했습니다. 실제로 내가 평소에 잊을 수도 있었지만 이번에는 그렇지 않았습니다. : P
Boris Callens

-1
    [HandleError]
    public class ErrorController : Controller
    {        
        [AcceptVerbs(HttpVerbs.Get)]
        public ViewResult NotAuthorized()
        {
            //401
            Response.StatusCode = (int)HttpStatusCode.Unauthorized;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult Forbidden()
    {
        //403
        Response.StatusCode = (int)HttpStatusCode.Forbidden;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult NotFound()
    {
        //404
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ViewResult ServerError()
    {
        //500
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.