[c#] ASP.NET MVC 사용자 지정 오류 처리 Application_Error Global.asax?

내 MVC 응용 프로그램의 오류를 확인하는 몇 가지 기본 코드가 있습니다. 현재 내 프로젝트에서 나는라는 컨트롤러가 Error동작 방법과를 HTTPError404(), HTTPError500()하고 General(). 모두 문자열 매개 변수를 error받습니다. 아래 코드를 사용하거나 수정합니다. 처리를 위해 데이터를 오류 컨트롤러에 전달하는 가장 좋은 / 올바른 방법은 무엇입니까? 가능한 한 강력한 솔루션을 갖고 싶습니다.

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Response.Clear();

    HttpException httpException = exception as HttpException;
    if (httpException != null)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        switch (httpException.GetHttpCode())
        {
            case 404:
                // page not found
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                // server error
                routeData.Values.Add("action", "HttpError500");
                break;
            default:
                routeData.Values.Add("action", "General");
                break;
        }
        routeData.Values.Add("error", exception);
        // clear error on server
        Server.ClearError();

        // at this point how to properly pass route data to error controller?
    }
}



답변

이를위한 새 경로를 만드는 대신 컨트롤러 / 작업으로 리디렉션하고 쿼리 문자열을 통해 정보를 전달할 수 있습니다. 예를 들면 :

protected void Application_Error(object sender, EventArgs e) {
  Exception exception = Server.GetLastError();
  Response.Clear();

  HttpException httpException = exception as HttpException;

  if (httpException != null) {
    string action;

    switch (httpException.GetHttpCode()) {
      case 404:
        // page not found
        action = "HttpError404";
        break;
      case 500:
        // server error
        action = "HttpError500";
        break;
      default:
        action = "General";
        break;
      }

      // clear error on server
      Server.ClearError();

      Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
    }

그러면 컨트롤러가 원하는 것을 수신합니다.

// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
   return View("SomeView", message);
}

접근 방식에는 몇 가지 장단점이 있습니다. 이러한 종류의 오류 처리에서 루핑에 매우주의하십시오. 다른 것은 asp.net 파이프 라인을 통해 404를 처리하기 때문에 모든 적중에 대한 세션 개체를 생성한다는 것입니다. 이는 많이 사용되는 시스템의 경우 문제 (성능)가 될 수 있습니다.


답변

초기 질문 “오류 컨트롤러에 routedata를 올바르게 전달하는 방법”에 대답하려면 다음을 수행하십시오.

IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

그런 다음 ErrorController 클래스에서 다음과 같은 함수를 구현하십시오.

[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Error(Exception exception)
{
    return View("Error", exception);
}

이것은 예외를 뷰로 푸시합니다. 보기 페이지는 다음과 같이 선언되어야합니다.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<System.Exception>" %>

그리고 오류를 표시하는 코드 :

<% if(Model != null) { %>  <p><b>Detailed error:</b><br />  <span class="error"><%= Helpers.General.GetErrorMessage((Exception)Model, false) %></span></p> <% } %>

다음은 예외 트리에서 모든 예외 메시지를 수집하는 함수입니다.

    public static string GetErrorMessage(Exception ex, bool includeStackTrace)
    {
        StringBuilder msg = new StringBuilder();
        BuildErrorMessage(ex, ref msg);
        if (includeStackTrace)
        {
            msg.Append("\n");
            msg.Append(ex.StackTrace);
        }
        return msg.ToString();
    }

    private static void BuildErrorMessage(Exception ex, ref StringBuilder msg)
    {
        if (ex != null)
        {
            msg.Append(ex.Message);
            msg.Append("\n");
            if (ex.InnerException != null)
            {
                BuildErrorMessage(ex.InnerException, ref msg);
            }
        }
    }


답변

Lion_cl에서 언급 한 ajax 문제에 대한 해결책을 찾았습니다.

global.asax :

protected void Application_Error()
    {
        if (HttpContext.Current.Request.IsAjaxRequest())
        {
            HttpContext ctx = HttpContext.Current;
            ctx.Response.Clear();
            RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
            rc.RouteData.Values["action"] = "AjaxGlobalError";

            // TODO: distinguish between 404 and other errors if needed
            rc.RouteData.Values["newActionName"] = "WrongRequest";

            rc.RouteData.Values["controller"] = "ErrorPages";
            IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
            IController controller = factory.CreateController(rc, "ErrorPages");
            controller.Execute(rc);
            ctx.Server.ClearError();
        }
    }

ErrorPagesController

public ActionResult AjaxGlobalError(string newActionName)
    {
        return new AjaxRedirectResult(Url.Action(newActionName), this.ControllerContext);
    }

AjaxRedirectResult

public class AjaxRedirectResult : RedirectResult
{
    public AjaxRedirectResult(string url, ControllerContext controllerContext)
        : base(url)
    {
        ExecuteResult(controllerContext);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            JavaScriptResult result = new JavaScriptResult()
            {
                Script = "try{history.pushState(null,null,window.location.href);}catch(err){}window.location.replace('" + UrlHelper.GenerateContentUrl(this.Url, context.HttpContext) + "');"
            };

            result.ExecuteResult(context);
        }
        else
        {
            base.ExecuteResult(context);
        }
    }
}

AjaxRequestExtension

public static class AjaxRequestExtension
{
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        return (request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest");
    }
}


답변

나는 전에 MVC 앱에서 전역 오류 처리 루틴을 중앙 집중화한다는 아이디어에 어려움을 겪었습니다. 나는이 는 ASP.NET 포럼에 게시물을 .

기본적으로 오류 컨트롤러, [HandlerError]속성 장식 또는 customErrorsweb.config 의 노드 조작없이 global.asax의 모든 애플리케이션 오류를 처리합니다 .


답변

MVC에서 오류를 처리하는 더 좋은 방법은 컨트롤러 또는 작업에 HandleError 특성을 적용하고 Shared / Error.aspx 파일을 업데이트하여 원하는 작업을 수행하는 것입니다. 해당 페이지의 Model 개체에는 Exception 속성과 ControllerName 및 ActionName이 포함됩니다.


답변

Ajax 요청에 문제가있는 Application_Error. Ajax가 호출 한 Action에서 오류가 처리되면 결과 컨테이너 내부에 오류보기가 표시됩니다.


답변

이것은 MVC ( https://stackoverflow.com/a/9461386/5869805 )에 가장 좋은 방법이 아닐 수 있습니다.

다음은 Application_Error에서 뷰를 렌더링하고 http 응답에 쓰는 방법입니다. 리디렉션을 사용할 필요가 없습니다. 이렇게하면 서버에 대한 두 번째 요청이 방지되므로 브라우저 주소 표시 줄의 링크가 동일하게 유지됩니다. 이것은 좋거나 나쁠 수 있으며 원하는 것에 따라 다릅니다.

Global.asax.cs

protected void Application_Error()
{
    var exception = Server.GetLastError();
    // TODO do whatever you want with exception, such as logging, set errorMessage, etc.
    var errorMessage = "SOME FRIENDLY MESSAGE";

    // TODO: UPDATE BELOW FOUR PARAMETERS ACCORDING TO YOUR ERROR HANDLING ACTION
    var errorArea = "AREA";
    var errorController = "CONTROLLER";
    var errorAction = "ACTION";
    var pathToViewFile = $"~/Areas/{errorArea}/Views/{errorController}/{errorAction}.cshtml"; // THIS SHOULD BE THE PATH IN FILESYSTEM RELATIVE TO WHERE YOUR CSPROJ FILE IS!

    var requestControllerName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["controller"]);
    var requestActionName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["action"]);

    var controller = new BaseController(); // REPLACE THIS WITH YOUR BASE CONTROLLER CLASS
    var routeData = new RouteData { DataTokens = { { "area", errorArea } }, Values = { { "controller", errorController }, {"action", errorAction} } };
    var controllerContext = new ControllerContext(new HttpContextWrapper(HttpContext.Current), routeData, controller);
    controller.ControllerContext = controllerContext;

    var sw = new StringWriter();
    var razorView = new RazorView(controller.ControllerContext, pathToViewFile, "", false, null);
    var model = new ViewDataDictionary(new HandleErrorInfo(exception, requestControllerName, requestActionName));
    var viewContext = new ViewContext(controller.ControllerContext, razorView, model, new TempDataDictionary(), sw);
    viewContext.ViewBag.ErrorMessage = errorMessage;
    //TODO: add to ViewBag what you need
    razorView.Render(viewContext, sw);
    HttpContext.Current.Response.Write(sw);
    Server.ClearError();
    HttpContext.Current.Response.End(); // No more processing needed (ex: by default controller/action routing), flush the response out and raise EndRequest event.
}

전망

@model HandleErrorInfo
@{
    ViewBag.Title = "Error";
    // TODO: SET YOUR LAYOUT
}
<div class="">
    ViewBag.ErrorMessage
</div>
@if(Model != null && HttpContext.Current.IsDebuggingEnabled)
{
    <div class="" style="background:khaki">
        <p>
            <b>Exception:</b> @Model.Exception.Message <br/>
            <b>Controller:</b> @Model.ControllerName <br/>
            <b>Action:</b> @Model.ActionName <br/>
        </p>
        <div>
            <pre>
                @Model.Exception.StackTrace
            </pre>
        </div>
    </div>
}