ASP.NET MVC에서 404를 올바르게 처리하려면 어떻게해야합니까?


432

RC2를 사용하고 있습니다

URL 라우팅 사용 :

routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

위의 요청은 다음과 같은 요청을 처리하는 것으로 보입니다 (초기 MVC 프로젝트에서 기본 라우팅 테이블 설정을 가정) : "/ blah / blah / blah / blah"

컨트롤러 자체에서 HandleUnknownAction () 재정의 :

// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
    ViewData["actionName"] = actionName;
    View("NotFound").ExecuteResult(this.ControllerContext);
}  

그러나 이전 전략은 Bad / Unknown 컨트롤러에 대한 요청을 처리하지 않습니다. 예를 들어, 나는 "/ IDoNotExist"를 가지고 있지 않다. 이것을 요청하면 웹 서버에서 일반 404 페이지를 얻습니다. 라우팅 + 재정의를 사용하면 404 페이지가 아닙니다.

마지막으로, 내 질문은 : MVC 프레임 워크 자체에서 경로 또는 다른 것을 사용하여 이러한 유형의 요청을 잡을 수있는 방법이 있습니까?

또는 기본적으로 Web.Config customErrors를 404 처리기로 사용 하고이 모든 것을 잊어 버려야합니까? customErrors를 사용하면 직접 액세스에 대한 Web.Config 제한으로 인해 / Views 외부에 일반 404 페이지를 저장해야한다고 가정합니다.


3
그것은 404 오류입니다. 난 그냥 신경 쓰지 않을 것입니다. 확실히 사용자가 잘못 입력 한 것으로 표시합니다 (404). 또는 그것이 이동 된 경우 응용 프로그램은 해당 요청을 받아 영구적으로 리디렉션해야합니다. 404는 응용 프로그램이 아닌 웹 서버에 속합니다. 오류가 발생하면 iis 페이지를 언제든지 사용자 정의 할 수 있습니다.
mamu

이 솔루션뿐만 아니라 blog.dantup.com/2009/04/…를
개발자


4
asp.net MVC + IIS에서 404를 처리하는 상황이 실제로 개선되지 않았으며이를 처리하는 방법에 대한 Q & A로 이동하는 것은 여전히 ​​부끄러운 일입니다.
joelmdev

답변:


271

코드는 http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx 에서 가져와 작동합니다 ASP.net MVC 1.0에서도

http 예외를 처리하는 방법은 다음과 같습니다.

protected void Application_Error(object sender, EventArgs e)
{
   Exception exception = Server.GetLastError();
   // Log the exception.

   ILogger logger = Container.Resolve<ILogger>();
   logger.Error(exception);

   Response.Clear();

   HttpException httpException = exception as HttpException;

   RouteData routeData = new RouteData();
   routeData.Values.Add("controller", "Error");

   if (httpException == null)
   {
       routeData.Values.Add("action", "Index");
   }
   else //It's an Http Exception, Let's handle it.
   {
       switch (httpException.GetHttpCode())
       {
          case 404:
              // Page not found.
              routeData.Values.Add("action", "HttpError404");
              break;
          case 500:
              // Server error.
              routeData.Values.Add("action", "HttpError500");
              break;

           // Here you can handle Views to other error codes.
           // I choose a General error template  
           default:
              routeData.Values.Add("action", "General");
              break;
      }
  }           

  // Pass exception details to the target error View.
  routeData.Values.Add("error", exception);

  // Clear the error on server.
  Server.ClearError();

  // Avoid IIS7 getting in the middle
  Response.TrySkipIisCustomErrors = true; 

  // Call target Controller and pass the routeData.
  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(    
       new HttpContextWrapper(Context), routeData));
}

23
업데이트 : http 404 확인이 반드시 필요하지만 500을 얻을 때까지 확실하지 않습니다. 또한 Response.StatusCode = 404 또는 500을 명시 적으로 설정해야합니다. 그렇지 않으면 Google 이이 페이지의 색인을 생성하기 시작합니다 이 코드는 현재 않는 200 상태 코드를 반환하는 경우
Simon_Weaver

6
@Simon_Weaver : 동의했다! 404 상태 코드를 반환해야합니다. 본질적으로 404 솔루션으로 깨졌습니다. 이것을 확인하십시오 : codinghorror.com/blog/2007/03/…
Matt Kocaj

1
이 전체 제안에는 근본적인 결함이 있습니다. 실행이 Global.asax로 버블 링 될 때 너무 많은 HttpContext가 누락되었습니다. 예제에서 제안한 것처럼 컨트롤러로 다시 라우팅 할 수 없습니다. 상단의 블로그 링크에서 주석을 참조하십시오.
Matt Kocaj

3
위의 의견 중 일부와 링크 된 게시물이 언급했듯이 이것은 작동하지 않는 것 같습니다. 오류 컨트롤러에 문제가 있지만 빈 화면이 나타납니다. (mvc 3 사용)
RyanW

4
MVC의 전체 목적은 모든 추상화를 제거하는 것이지만 다시 여기에 있습니다.
Alex Nolasco

255

404 요구 사항

다음은 404 솔루션에 대한 요구 사항이며 아래에서 구현 방법을 보여줍니다.

  • 나쁜 행동으로 일치하는 경로를 처리하고 싶습니다.
  • 불량 컨트롤러로 일치하는 경로를 처리하고 싶습니다.
  • 일치하지 않는 경로 (앱이 이해할 수없는 임의의 URL)를 처리하고 싶습니다 .Global.asax 또는 IIS까지 버블 링을 원하지 않습니다. 그러면 MVC 앱으로 올바르게 리디렉션 할 수 없기 때문입니다
  • 존재하지 않는 객체에 대해 ID를 제출할 때와 같이 위와 같은 방식으로 사용자 정의 404를 처리하는 방법을 원합니다.
  • 난 내가 이상 (필요한 경우 더 많은 데이터를 펌핑 할 수있는 할 수있는 MVC 뷰 (안 정적 페이지)를 돌려 내 모든 404을 원하는 좋은 404 개 디자인 ) 그들이 있어야 는 HTTP 404 상태 코드를 반환

해결책

Application_Error처리되지 않은 예외 및 로깅 ( Shay Jacoby의 답변 쇼 와 같은)과 같은 더 높은 것들을 위해 Global.asax에 저장해야 하지만 404 처리는하지 않아야한다고 생각합니다 . 이것이 내 제안이 Global.asax 파일에서 404 항목을 유지하는 이유입니다.

1 단계 : 404 오류 논리의 공통 위치

이것은 유지 관리에 좋은 아이디어입니다. 잘 설계된 404 페이지의 향후 개선 사항을 쉽게 적용 할 수 있도록 ErrorController를 사용하십시오 . 또한 응답에 404 코드가 있는지 확인하십시오 !

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // If the url is relative ('NotFound' route) then replace with Requested path
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Dont get the user stuck in a 'retry loop' by
        // allowing the Referrer to be the same as the Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: insert ILogger here

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}

2 단계 : 기본 Controller 클래스를 사용하여 사용자 정의 404 조치를 쉽게 호출하고 연결하십시오. HandleUnknownAction

ASP.NET MVC의 404는 여러 곳에서 포착해야합니다. 첫 번째는 HandleUnknownAction입니다.

InvokeHttp404방법은 ErrorController새로운 Http404작업으로 다시 라우팅 할 수있는 일반적인 장소를 만듭니다 . 건조를 생각하십시오 !

public abstract class MyController : Controller
{
    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        // If controller is ErrorController dont 'nest' exceptions
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

3 단계 : Controller Factory에서 Dependency Injection을 사용하고 404 HttpExceptions 연결

이와 같이 (StructureMap 일 필요는 없음) :

MVC1.0 예 :

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

MVC2.0 예 :

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == 404)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }

오류가 발생한 위치에 더 가깝게 오류를 잡는 것이 좋습니다. 이것이 내가 Application_Error핸들러 보다 위의 것을 선호하는 이유 입니다.

이것은 404를 잡는 두 번째 장소입니다.

4 단계 : 앱으로 구문 분석되지 않은 URL에 대해 NotFound 경로를 Global.asax에 추가

이 경로는 우리의 Http404행동을 가리켜 야합니다 . 통지 url라우팅 엔진이 여기에 도메인 부분을 제거하기 때문에 PARAM 상대 URL 될 것인가? 이것이 1 단계에서 모든 조건부 URL 논리를 갖는 이유입니다.

        routes.MapRoute("NotFound", "{*url}", 
            new { controller = "Error", action = "Http404" });

MVC 앱에서 404를 잡을 수있는 세 번째이자 마지막 장소입니다. 여기서 일치하지 않는 경로를 찾지 못하면 MVC가 문제를 ASP.NET (Global.asax)으로 전달하므로이 상황에서는 실제로 원하지 않습니다.

5 단계 : 마지막으로 앱에서 무언가를 찾을 수 없을 때 404를 호출합니다.

잘못된 ID가 대출 컨트롤러에 제출 될 때와 같이 (에서 파생 됨 MyController) :

    //
    // GET: /Detail/ID

    public ActionResult Detail(int ID)
    {
        Loan loan = this._svc.GetLoans().WithID(ID);
        if (loan == null)
            return this.InvokeHttp404(HttpContext);
        else
            return View(loan);
    }

이 모든 것이 적은 코드로 적은 수의 장소에 연결될 수 있다면 좋을 것입니다.하지만이 솔루션은 유지 관리가 쉽고 테스트 가능하며 실용적이라고 생각합니다.

지금까지 의견을 보내 주셔서 감사합니다. 더 많은 것을 얻고 싶습니다.

참고 : 이것은 원래의 답변에서 크게 편집되었지만 목적 / 요구 사항은 동일합니다-이것이 새로운 답변을 추가하지 않은 이유입니다


12
전체 작성 감사합니다. IIS7에서 실행할 때 "TrySkipIisCustomErrors"속성을 true로 설정해야합니다. 그렇지 않으면 IIS는 여전히 기본 404 페이지를 반환합니다. Response.TrySkipIiisCustomErrors = true를 추가했습니다. 상태 코드를 설정하는 5 단계의 행 뒤에 msdn.microsoft.com/ko-kr/library/…
Rick

1
@Ryan customErrorsweb.config 의 섹션은 IIS가 아닌 경우 aspnet에서 높은 수준으로 처리되는 정적 리디렉션 페이지를 정의합니다. 이것은 내가 결과를 MVC 뷰로 만들어야하기 때문에 원하는 것이 아닙니다 (따라서 데이터 등을 가질 수 있습니다). 나는 " customErrorsMVC에서는 쓸모 가 없다"고 말하지 는 않지만 저와이 404 솔루션에는 확실히 있습니다.
Matt Kocaj

1
또한 누군가가 3 단계를 업데이트하여 StructureMap이 사용되지 않습니까? ControllerController를 아직 사용하지 않는 경우 구현하기 쉬운 일반 ControllerFactory 일 수 있습니다.
David Murdoch

7
이것은 MVC3에 잘 작동합니다. 대신 ObjectFactory.GetInstanceMVC3으로 전환 하여 DependencyResolver.Current.GetService보다 일반적입니다. Ninject를 사용하고 있습니다.
kamranicus

122
다른 사람은 웹 프레임 워크에서 404와 같은 일반적인 것이 너무 피의 복잡하다는 것이 특허 적으로 미친 것을 발견합니까?
quentin-starin

235

ASP.NET MVC는 사용자 지정 404 페이지를 잘 지원하지 않습니다. 맞춤형 컨트롤러 공장, 포괄 루트, 기본 컨트롤러 클래스 HandleUnknownAction-argh!

지금까지 IIS 사용자 정의 오류 페이지가 더 나은 대안입니다.

web.config

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" />
    <error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
  </httpErrors>
</system.webServer>

ErrorController

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

샘플 프로젝트


38
이 답변은 승인 된 답변입니다! IIS Express가 포함 된 ASP.NET MVC 3에서 탁월한 성능을 발휘합니다.
Andrei Rînea

7
IIS7 +를 사용하고 있다면 이것이 확실한 방법입니다. +1!
elo80ka

3
동일한 프로젝트 내에서 JSON으로 작업 할 때 상태 404 만 반환 할 수 있습니까?
VinnyG

6
이것은 iis express에서 훌륭하게 작동하지만 프로덕션 IIS 7.5에 사이트를 배포하자마자 오류보기 대신 흰색 페이지가 표시됩니다.
Moulde

2
(MVC3 포함) 내 테스트에이 휴식을 따르면 customErrors mode="On"와 함께하는 HandleErrorAttribute기능을한다. 컨트롤러 작업에서 처리되지 않은 예외에 대한 사용자 지정 오류 페이지는 더 이상 제공되지 않습니다.
Slauma

153

빠른 답변 / TL; DR

여기에 이미지 설명을 입력하십시오

게으른 사람들을 위해 :

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

그런 다음이 줄을 제거하십시오. global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

그리고 이것은 IIS7 + 및 IIS Express에만 해당됩니다.

Cassini를 사용하는 경우 .. 음 .. 어 .. 어색한 ... 어색한


길고 설명 된 답변

나는 이것이 대답되었다는 것을 안다. 그러나 그 대답은 정말 간단합니다 ( David FowlerDamian Edwards 에게 정말로 응답 해주었습니다).

사용자 정의 작업을 수행 할 필요없습니다 .

의 경우 ASP.NET MVC3모든 비트와 조각이 있습니다.

1 단계-> 두 개의 지점에서 web.config를 업데이트하십시오.

<system.web>
    <customErrors mode="On" defaultRedirect="/ServerError">
      <error statusCode="404" redirect="/NotFound" />
    </customErrors>

<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
    </httpErrors>    

...
<system.webServer>
...
</system.web>

이제 사용하기로 결정한 경로를주의해서 기록하십시오. 당신은 아무것도 사용할 수 있지만 내 경로는

  • /NotFound <-404를 찾을 수 없으면 오류 페이지입니다.
  • /ServerError<-다른 오류의 경우 내 코드에서 발생하는 오류를 포함하십시오. 이것은 500 내부 서버 오류입니다

첫 번째 섹션에 하나의 사용자 정의 항목 <system.web>만 있는 방법을 참조하십시오 항목? 나는 오직 하나 개의 상태 코드를 나열했습니다 때문에 포함한 다른 모든 오류 .. 다른 모든 오류가 설정에 의해 처리됩니다 (코드 버그를 가지고 있으며, 사용자의 요청을 충돌 때 발생하는 즉. 그 성가신 오류) 라고 .. , 404 페이지를 찾을 수없는 경우 경로로 이동하십시오 .statusCode="404"500 Server ErrordefaultRedirect="/ServerError"/ServerError

확인. 그 길을 벗어났습니다 .. 지금 내 경로에 나열된global.asax

2 단계-Global.asax에서 경로 생성

여기 전체 경로 섹션이 있습니다.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});

    routes.MapRoute(
        "Error - 404",
        "NotFound",
        new { controller = "Error", action = "NotFound" }
        );

    routes.MapRoute(
        "Error - 500",
        "ServerError",
        new { controller = "Error", action = "ServerError"}
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
        );
}

여기에는 두 개의 무시 경로가 표시됩니다-> axd'sfavicons(ooo! 보너스 무시 경로, 당신을 위해!) 그런 다음 (그리고 순서는 여기에서 중요합니다), 나는 두 개의 명시 적 오류 처리 경로가 있습니다. 이 경우 기본 설정입니다. 물론, 나는 더 많은 것을 가지고 있지만 그것은 내 웹 사이트에 특별합니다. 오류 경로가 목록의 맨 위에 있는지 확인하십시오. 순서는 필수적 입니다.

마지막으로 global.asax파일 내부에있는 동안 HandleError 특성을 전역 적으로 등록하지 않습니다. 아뇨, 아뇨 나다. 아니. 니엔 부정. 안돼 ...

이 줄을 제거하십시오 global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

3 단계-조치 방법으로 제어기 작성

이제 .. 우리는 두 가지 행동 방법으로 컨트롤러를 추가합니다 ...

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

    public ActionResult ServerError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Todo: Pass the exception into the view model, which you can make.
        //       That's an exercise, dear reader, for -you-.
        //       In case u want to pass it to the view, if you're admin, etc.
        // if (User.IsAdmin) // <-- I just made that up :) U get the idea...
        // {
        //     var exception = Server.GetLastError();
        //     // etc..
        // }

        return View();
    }

    // Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
    public ActionResult ThrowError()
    {
        throw new NotImplementedException("Pew ^ Pew");
    }
}

좋아, 이것 좀 봐 우선, 여기 에는 속성 이 없습니다 [HandleError] . 왜? 내장 된 ASP.NET프레임 워크는 이미 오류를 처리 하고 있기 때문에 오류를 처리하기 위해 해야하는 모든 똥을 지정했습니다. :)이 방법에 있습니다!

다음으로 두 가지 행동 방법이 있습니다. 거기에 힘든 것은 없습니다. 예외 정보를 표시하려면 Server.GetLastError()해당 정보를 얻는 데 사용할 수 있습니다 .

보너스 WTF : 예, 오류 처리를 테스트하기 위해 세 번째 조치 방법을 만들었습니다.

4 단계-보기 작성

마지막으로 두 개의 뷰를 만듭니다. 이 컨트롤러에 대해 정상적인 시점에 배치하십시오.

여기에 이미지 설명을 입력하십시오

보너스 코멘트

  • 당신은 필요하지 않습니다 Application_Error(object sender, EventArgs e)
  • 위의 단계는 모두 Elmah 와 100 % 완벽하게 작동 합니다. 엘마 프레이크 스 약!

그리고 그것은 내 친구들입니다.

자, 이것을 많이 읽은 것을 축하하고 유니콘을 상으로 삼았습니다!

여기에 이미지 설명을 입력하십시오


그래서 이것을 구현하려고했지만 몇 가지 문제가 있습니다 ... 먼저 weeb.config의 경로 앞에 ~가 필요하거나 가상 디렉토리에서 작동하지 않습니다. 2-IIS 사용자 지정 오류가 발생하고 뷰가 레이아웃을 사용하는 경우 전혀 렌더링되지 않고 흰색 페이지 만 표시됩니다. 이 줄을 컨트롤러에 추가함으로써 "Response.TrySkipIisCustomErrors = true;" . 그러나 mysite / whatever / fake.html 과 같이 파일이지만 404 .. 인 URL로 이동하면 여전히 작동하지 않습니다 .
Robert Noack 2016 년

3
-1, 죄송합니다. 404의 URL을 변경하는 솔루션이 잘못되었습니다. webconfig를 사용하면 MVC에서 URL을 변경하지 않고 처리 할 수있는 방법이 없거나 정적 html 파일 또는 aspx (예 : 일반 aspx 파일)를 만들어야합니다. ?aspxerrorpath=/er/not/foundURL을 갖고 싶다면 솔루션이 좋습니다 .
Gutek

7
정말 이상하게 들릴지 모르지만 제 답변은 오래 전에 제공되었으며 귀하의 @Gutek에 동의합니다 . 더 이상 오류 페이지로 리디렉션하는 것을 좋아하지 않습니다 . 나는 (내 대답 : P 참조)에 익숙했다. 오류가 / some / resource에서 발생한 경우 .. 해당 리소스는 404 또는 500 등을 반환해야합니다. 그렇지 않으면 대규모 SEO 관련 사항이 있습니다. Ahh .. 시간이 어떻게
변하는가

@Gutek customErrors redirectMode = "ResponseRewrite"를 알고 있습니까? 그리고 보안 관점에서 404를 반환하는 것은 이상적이지 않습니다
Jowen

1
@Chris <좋아하는 신을 여기에 삽입> 젠장. 나는 그것이 지금 무엇인지 기억조차 못합니다. 글쎄, 구출에 대한 내 밈 수집 ..... 고정.
Pure.Krome

86

MVC (특히 MVC3) 에서 404를 올바르게 관리하는 방법에 대해 많은 것을 조사 했으며 IMHO는 내가 생각해 낸 최고의 솔루션입니다.

global.asax에서 :

public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
            rd.Values["controller"] = "Errors";
            rd.Values["action"] = "NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

ErrorsController :

public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

(선택 과목)

설명:

AFAIK에는 ASP.NET MVC3 앱이 404를 생성 할 수있는 6 가지 경우가 있습니다.

(ASP.NET Framework에서 자동 생성 :)

(1) URL이 라우팅 테이블에서 일치하는 것을 찾지 않습니다.

(ASP.NET MVC 프레임 워크에 의해 자동으로 생성됨)

(2) URL이 라우팅 테이블에서 일치하는 것을 찾지 만 존재하지 않는 컨트롤러를 지정합니다.

(3) URL은 라우팅 테이블에서 일치하는 항목을 찾지 만 존재하지 않는 작업을 지정합니다.

(수동 생성 :)

(4) 액션은 HttpNotFound () 메소드를 사용하여 HttpNotFoundResult를 반환합니다.

(5) 액션은 상태 코드가 404 인 HttpException을 발생시킵니다.

(6) 작업은 Response.StatusCode 속성을 404로 수동으로 수정합니다.

일반적으로 3 가지 목표를 달성하려고합니다.

(1) 사용자에게 404 오류 페이지를 보여줍니다.

(2) 클라이언트 응답에 404 상태 코드를 유지하십시오 (특히 SEO에 중요 함).

(3) 302 리디렉션을 포함하지 않고 직접 응답을 보냅니다.

이를 달성하기 위해 여러 가지 방법이 있습니다.

(1)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

이 솔루션의 문제점 :

  1. (1), (4), (6)의 경우 목표 (1)을 준수하지 않습니다.
  2. 목표 (2)를 자동으로 준수하지 않습니다. 수동으로 프로그래밍해야합니다.
  3. 목표 (3)을 준수하지 않습니다.

(2)

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

이 솔루션의 문제점 :

  1. IIS 7 이상에서만 작동합니다.
  2. 사례 (2), (3), (5)에서 목표 (1)을 준수하지 않습니다.
  3. 목표 (2)를 자동으로 준수하지 않습니다. 수동으로 프로그래밍해야합니다.

(삼)

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

이 솔루션의 문제점 :

  1. IIS 7 이상에서만 작동합니다.
  2. 목표 (2)를 자동으로 준수하지 않습니다. 수동으로 프로그래밍해야합니다.
  3. 응용 프로그램 레벨 http 예외를 숨 깁니다. 예를 들어 customErrors 섹션, System.Web.Mvc.HandleErrorAttribute 등은 사용할 수 없습니다. 일반적인 오류 페이지 만 표시 할 수는 없습니다.

(4)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

이 솔루션의 문제점 :

  1. IIS 7 이상에서만 작동합니다.
  2. 목표 (2)를 자동으로 준수하지 않습니다. 수동으로 프로그래밍해야합니다.
  3. (2), (3), (5)의 경우 목표 (3)을 준수하지 않습니다.

전에 문제가있는 사람들은 자신의 라이브러리를 만들려고 시도했습니다 ( http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html 참조 ). 그러나 이전 솔루션은 외부 라이브러리 사용의 복잡성없이 모든 경우를 다루는 것으로 보입니다.


좋은 대답입니다. 더 많은 공감대가 필요합니다. global.asax 코드가 Application_Error에서 작동하지 않는 이유는 무엇입니까?
NinjaNye

7
감사! 컨트롤러에서 발생하는 명시 적 404는 ASP.NET의 오류로 간주되지 않으므로 Application_Error에서 수행 할 수 없습니다. 컨트롤러에서 HttpNotFound ()를 반환하면 Application_Error 이벤트가 트리거되지 않습니다.
Marco

1
나는 public ActionResult NotFound() {}당신이 ErrorsController 를 잊어 버렸다고 생각합니다 . 또한 _NotFoundAJAX 요청에 대한 부분의 모습 을 설명 할 수 있습니까?
d4n3

2
MVC 4를 사용하면 항상 MissingMethodException: Cannot create an abstract class온라인에 접속할 수 있습니다. c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); 어떤 아이디어?
µBio November

1
"찾을 수 없음"URL에 경로에 점이 포함되어 있으면 (예 : example.com/hi.bob ) Application_EndRequest가 전혀 실행되지 않으며 IE의 일반 404 페이지가 표시됩니다.
Bob.at.Indigo.Health

13

나는 cottsaks 솔루션을 정말 좋아하고 매우 명확하게 설명되어 있다고 생각합니다. 내 유일한 추가는 다음과 같이 2 단계를 변경하는 것이 었습니다.

public abstract class MyController : Controller
{

    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        //if controller is ErrorController dont 'nest' exceptions
        if(this.GetType() != typeof(ErrorController))
        this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

기본적으로 이는 유효하지 않은 조치 및 컨트롤러를 포함하는 URL이 예외 루틴을 두 번 트리거하지 못하게합니다. 예 : asdfsdf / dfgdfgd와 같은 URL의 경우


4
이것은 우수하다. 그 "두 번"의 사건은 나를 귀찮게하기 시작했다. 내 답변을 업데이트
Matt Kocaj

사용자가 잘못된 컨트롤러 및 작업 이름을 입력하면 위의 솔루션이 전혀 작동하지 않습니까?
Monojit Sarkar

6

잘못된 컨트롤러에 대해 @cottsak의 메소드를 얻을 수있는 유일한 방법은 다음과 같이 CustomControllerFactory에서 기존 경로 요청을 수정하는 것입니다.

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType); 
            else
                return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                requestContext.RouteData.Values["controller"] = "Error";
                requestContext.RouteData.Values["action"] = "Http404";
                requestContext.RouteData.Values.Add("url", requestContext.HttpContext.Request.Url.OriginalString);

                return ObjectFactory.GetInstance<ErrorController>();
            }
            else
                throw ex;
        }
    }
}

MVC 2.0을 사용하고 있다고 언급해야합니다.


왜 그런지 아십니까? (MVC2 특정?)
Matt Kocaj

핵심은 기존 요청을 새로 만드는 것이 아니라 수정하는 것이었지만, 조금 전에이 작업을 수행 했으므로 확실하지 않습니다. 컨트롤러 팩토리에서 "InvokeHttp404"가 작동하지 않았습니다.
Dave K

오늘 MVC2 관련 내용으로 답변을 업데이트했습니다. 위에서 설명한 내 솔루션이 여전히 작동하지 않는 경우 알려주십시오.
매트 코카 지

4

다음은 MVC 도구를 사용하여 잘못된 컨트롤러 이름, 잘못된 경로 이름 및 Action 메서드 내부에 적합한 다른 기준에 대한 요청을 처리 할 수있는 다른 방법입니다. 개인적으로 302 / 200 리디렉션을 수행 Server.Transfer하고 Razor보기를 사용하여 ResponseRewrite ( )를 지원하지 않기 때문에 가능한 많은 web.config 설정을 피하는 것이 좋습니다. SEO 이유로 커스텀 오류 페이지가있는 404를 반환하고 싶습니다.

이 중 일부는 위의 cottsak 기술을 새롭게 도입 한 것입니다.

이 솔루션은 대신 MVC 3 오류 필터를 선호하는 최소 web.config 설정을 사용합니다.

용법

액션이나 커스텀 ActionFilterAttribute에서 HttpException을 던지십시오.

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

1 단계

web.config에 다음 설정을 추가하십시오. MVC의 HandleErrorAttribute를 사용해야합니다.

<customErrors mode="On" redirectMode="ResponseRedirect" />

2 단계

HTTP 오류를 제외하고 MVC 프레임 워크의 HandleErrorAttribute와 유사한 사용자 정의 HandleHttpErrorAttribute를 추가하십시오.

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

3 단계

GlobalFilterCollection (에 필터를 추가 GlobalFilters.Filters에서) Global.asax. 이 예제는 모든 InternalServerError (500) 오류를 오류 공유보기 ( Views/Shared/Error.vbhtml)로 라우팅합니다 . NotFound (404) 오류는 공유보기에서도 ErrorHttp404.vbhtml로 전송됩니다. 여기에 401 오류를 추가하여 HTTP 오류 코드를 추가로 확장하는 방법을 보여줍니다. 이 뷰는 공유 뷰 여야하며 모두 System.Web.Mvc.HandleErrorInfo객체를 모델로 사용합니다 .

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

4 단계

기본 컨트롤러 클래스를 생성하고 컨트롤러에서 상속하십시오. 이 단계를 통해 알 수없는 작업 이름을 처리하고 HandleHttpErrorAttribute로 HTTP 404 오류를 발생시킬 수 있습니다.

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

5 단계

ControllerFactory 재정의를 만들고 Application_Start의 Global.asax 파일에서 재정의하십시오. 이 단계를 통해 유효하지 않은 컨트롤러 이름을 지정한 경우 HTTP 404 예외를 발생시킬 수 있습니다.

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

6 단계

BaseController Unknown 액션에 대한 RoutTable.Routes에 특별한 경로를 포함하십시오. 이것은 사용자가 알 수없는 컨트롤러 또는 알 수없는 작업에 액세스하는 경우 404를 올리는 데 도움이됩니다.

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

요약

이 예제는 MVC 프레임 워크를 사용하여 필터 속성 및 공유 오류보기를 사용하여 경로 재지 정하지 않고 브라우저에 404 Http 오류 코드를 리턴하는 방법을 보여줍니다. 또한 유효하지 않은 제어기 이름과 조치 이름이 지정된 경우 동일한 사용자 정의 오류 페이지를 표시합니다.

유효하지 않은 컨트롤러 이름, 작업 이름 및 Home / TriggerNotFound 작업에서 발생하는 사용자 지정 404의 스크린 샷을 추가하여 하나를 게시 할 수있는 충분한 투표를 얻는 경우 =). 이 솔루션을 사용하여 다음 URL에 액세스하면 Fiddler가 404 메시지를 반환합니다.

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

위의 cottsak의 게시물과 이러한 기사는 좋은 참고 자료였습니다.


흠, 나는 이것을 작동시키지 못했습니다 : The IControllerFactory 'aaa.bbb.CustomControllerFactory' did not return a controller for the name '123'.-내가 그것을 얻는 이유는 무엇입니까?
enashnash

redirectMode = "ResponseRedirect". 이것은 SEO에 좋지 않은 302 Found + 200 OK를 반환합니다!
PussInBoots

4

처리되지 않은 영역, 컨트롤러 및 작업에서 작동하는 단축 솔루션 :

  1. 보기 404.cshtml을 작성하십시오.

  2. 컨트롤러의 기본 클래스를 작성하십시오.

    public class Controller : System.Web.Mvc.Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            Http404().ExecuteResult(ControllerContext);
        }
    
        protected virtual ViewResult Http404()
        {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return View("404");
        }
    }
  3. 대체 컨트롤러로 기본 컨트롤러를 반환하는 사용자 정의 컨트롤러 팩토리를 만듭니다.

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
  4. Application_Start()다음 줄에 추가하십시오 .

    ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));

3

MVC4에서 WebAPI 404는 다음과 같은 방식으로 처리 할 수 ​​있습니다.

코스 APICONTROLLER

    // GET /api/courses/5
    public HttpResponseMessage<Courses> Get(int id)
    {
        HttpResponseMessage<Courses> resp = null;

        var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault();

        resp = aCourse == null ? new HttpResponseMessage<Courses>(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage<Courses>(aCourse);

        return resp;
    }

가정 관제사

public ActionResult Course(int id)
{
    return View(id);
}

전망

<div id="course"></div>
<script type="text/javascript">
    var id = @Model;
    var course = $('#course');
    $.ajax({    
        url: '/api/courses/' + id,
        success: function (data) {
            course.text(data.Name);
        },
        statusCode: {
            404: function() 
            {
                course.text('Course not available!');    
            }
        }
    });
</script>

글로벌

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

결과

여기에 이미지 설명을 입력하십시오


2

너겟에서 NotFoundMVC를 사용해보십시오. 작동하지만 설정이 없습니다.


http://localhost/Views/Shared/NotFound.cshtml사용자 정의 404 페이지가 생성되지 않습니다.
Dan Friedman

매우 쉽게 사용자 정의 할 수 있습니다. 요청한 URL과 리퍼러에 액세스 할 수 있으므로 원하는 것을 수행 할 수 있습니다. 이 패키지를 사용하는데 정말 효과가 있습니다.
Avrohom Yisroel

이 패키지는 async Task <ActionResult> 작업 (또는 다른 유사한 async) 작업을 사용하지 않는 훌륭한 패키지입니다. MVC 5에서 이것은 깨진 시나리오입니다. 그것을 피하기 위해 GitHub에 포크가 있지만 나에게는 그렇지 않습니다.
Stargazer

2

누군가가 유용하다고 생각하는 경우 내 솔루션.

Web.config에서 :

<system.web>
    <customErrors mode="On" defaultRedirect="Error" >
      <error statusCode="404" redirect="~/Error/PageNotFound"/>
    </customErrors>
    ...
</system.web>

에서 Controllers/ErrorController.cs:

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        if(Request.IsAjaxRequest()) {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return Content("Not Found", "text/plain");
        }

        return View();
    }
}

폴더에를 추가하면 PageNotFound.cshtml됩니다 Shared.


2
이로 인해 클라이언트로 302 리디렉션 후 200 (확인) 상태가 발생하지 않습니까? 여전히 404 등급이 아니어야합니까?
Sam

@Konamiman 당신은 확실히 코드에서 줄을 읽어야 있습니까 model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ? Request.Url.OriginalString : url;하지 model.RequestedUrl = Request.Url.OriginalString.Contains(url) && Request.Url.OriginalString != url ? Request.Url.OriginalString : url;(대신 &&)?
Jean-François Beauchamp

2

그러나 내부 구성 이 MVC와 호환되지 않는 것 때문에 표준 CustomErrors구성 이 제대로 작동Server.Transfer 하는 것 같습니다 ResponseRewrite.

이것은 나에게 눈에 띄는 기능 구멍처럼 느껴지므로 HTTP 모듈을 사용 하여이 기능을 다시 구현하기로 결정했습니다. 아래 솔루션을 사용하면 평소처럼 유효한 MVC 경로로 리디렉션하여 모든 HTTP 상태 코드 (404 포함)를 처리 할 수 ​​있습니다.

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="404.aspx" />
    <error statusCode="500" redirect="~/MVCErrorPage" />
</customErrors>

이것은 다음 플랫폼에서 테스트되었습니다.

  • 통합 파이프 라인 모드의 MVC4 (IIS Express 8)
  • 클래식 모드의 MVC4 (VS Development Server, Cassini)
  • 클래식 모드의 MVC4 (IIS6)

혜택

  • 모든 MVC 프로젝트에 적용 할 수있는 일반 솔루션
  • 전통적인 사용자 정의 오류 구성 지원
  • 통합 파이프 라인 및 클래식 모드에서 작동

해결책

namespace Foo.Bar.Modules {

    /// <summary>
    /// Enables support for CustomErrors ResponseRewrite mode in MVC.
    /// </summary>
    public class ErrorHandler : IHttpModule {

        private HttpContext HttpContext { get { return HttpContext.Current; } }
        private CustomErrorsSection CustomErrors { get; set; }

        public void Init(HttpApplication application) {
            System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
            CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");

            application.EndRequest += Application_EndRequest;
        }

        protected void Application_EndRequest(object sender, EventArgs e) {

            // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
            if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {

                int statusCode = HttpContext.Response.StatusCode;

                // if this request has thrown an exception then find the real status code
                Exception exception = HttpContext.Error;
                if (exception != null) {
                    // set default error status code for application exceptions
                    statusCode = (int)HttpStatusCode.InternalServerError;
                }

                HttpException httpException = exception as HttpException;
                if (httpException != null) {
                    statusCode = httpException.GetHttpCode();
                }

                if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {

                    Dictionary<int, string> errorPaths = new Dictionary<int, string>();

                    foreach (CustomError error in CustomErrors.Errors) {
                        errorPaths.Add(error.StatusCode, error.Redirect);
                    }

                    // find a custom error path for this status code
                    if (errorPaths.Keys.Contains(statusCode)) {
                        string url = errorPaths[statusCode];

                        // avoid circular redirects
                        if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {

                            HttpContext.Response.Clear();
                            HttpContext.Response.TrySkipIisCustomErrors = true;

                            HttpContext.Server.ClearError();

                            // do the redirect here
                            if (HttpRuntime.UsingIntegratedPipeline) {
                                HttpContext.Server.TransferRequest(url, true);
                            }
                            else {
                                HttpContext.RewritePath(url, false);

                                IHttpHandler httpHandler = new MvcHttpHandler();
                                httpHandler.ProcessRequest(HttpContext);
                            }

                            // return the original status code to the client
                            // (this won't work in integrated pipleline mode)
                            HttpContext.Response.StatusCode = statusCode;

                        }
                    }

                }

            }

        }

        public void Dispose() {

        }


    }

}

용법

이것을 web.config의 최종 HTTP 모듈로 포함

  <system.web>
    <httpModules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </httpModules>
  </system.web>

  <!-- IIS7+ -->
  <system.webServer>
    <modules>
      <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
    </modules>
  </system.webServer>

주의를 기울이는 사용자에게는 통합 파이프 라인 모드에서 Server.TransferRequest작동 방식으로 인해 항상 HTTP 200으로 응답한다는 것을 알 수 있습니다. 올바른 오류 코드를 반환하려면 다음 오류 컨트롤러를 사용하십시오.

public class ErrorController : Controller {

    public ErrorController() { }

    public ActionResult Index(int id) {
        // pass real error code to client
        HttpContext.Response.StatusCode = id;
        HttpContext.Response.TrySkipIisCustomErrors = true;

        return View("Errors/" + id.ToString());
    }

}

2

ASP.NET MVC의 오류를 다루는 것은 엉덩이에 고통입니다. 이 페이지와 다른 질문과 사이트에서 많은 제안을 시도했지만 아무런 효과가 없습니다. 한 가지 제안은 system.webserver 내의 web.config에서 오류를 처리하는 것이지만 빈 페이지반환합니다 .

이 솔루션을 만들 때 나의 목표는;

  • 리디렉션하지 않음
  • 기본 오류 처리와 같이 200 / Ok가 아닌 적절한 상태 코드 반환

여기 내 해결책이 있습니다.

1. system.web 섹션에 다음을 추가 하십시오.

   <system.web>
     <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404"  redirect="~/Error/404.aspx" />
      <error statusCode="500" redirect="~/Error/500.aspx" />
     </customErrors>
    <system.web>

위의 경우 routes.config 에서 처리 하지 않은 URL 과 처리되지 않은 예외, 특히 뷰에서 발생하는 예외를 처리합니다. html이 아닌 aspx를 사용했습니다 . 이 코드 뒤에 응답 코드 를 추가 할 수 있습니다 .

2 . 프로젝트의 루트에 Error (또는 원하는 것) 라는 폴더를 작성 하고 두 개의 웹 양식을 추가하십시오. 아래는 내 404 페이지입니다.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="404.aspx.cs" Inherits="Myapp.Error._404" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title >Page Not found</title>
    <link href="<%=ResolveUrl("~/Content/myapp.css")%>" rel="stylesheet" />
</head>
<body>
    <div class="top-nav">
      <a runat="server" class="company-logo" href="~/"></a>
    </div>
    <div>
        <h1>404 - Page Not found</h1>
        <p>The page you are looking for cannot be found.</p>
        <hr />
        <footer></footer>
    </div>
</body>
</html>

코드 뒤에는 응답 코드를 설정했습니다.

protected void Page_Load(object sender, EventArgs e)
{
    Response.StatusCode = 404;
}

500 페이지에 대해서도 동일하게 수행

3. 컨트롤러 내의 오류를 처리합니다. 여러 가지 방법이 있습니다. 이것이 나를 위해 일한 것입니다. 모든 컨트롤러는 기본 컨트롤러에서 상속됩니다. 기본 컨트롤러에는 다음과 같은 방법이 있습니다

protected ActionResult ShowNotFound()
{
    return ShowNotFound("Page not found....");
}

protected ActionResult ShowNotFound(string message)
{
    return ShowCustomError(HttpStatusCode.NotFound, message);
}

protected ActionResult ShowServerError()
{
    return ShowServerError("Application error....");
}

protected ActionResult ShowServerError(string message)
{
    return ShowCustomError(HttpStatusCode.InternalServerError, message);
}

protected ActionResult ShowNotAuthorized()
{
    return ShowNotAuthorized("You are not allowed ....");

}

protected ActionResult ShowNotAuthorized(string message)
{
    return ShowCustomError(HttpStatusCode.Forbidden, message);
}

protected ActionResult ShowCustomError(HttpStatusCode statusCode, string message)
{
    Response.StatusCode = (int)statusCode;
    string title = "";
    switch (statusCode)
    {
        case HttpStatusCode.NotFound:
            title = "404 - Not found";
            break;
        case HttpStatusCode.Forbidden:
            title = "403 - Access Denied";
            break;
        default:
            title = "500 - Application Error";
            break;
    }
    ViewBag.Title = title;
    ViewBag.Message = message;
    return View("CustomError");
}

4. CustomError.cshtml을 공유 보기 폴더에 추가하십시오. 아래는 내 것입니다.

<h1>@ViewBag.Title</h1>
<br />
<p>@ViewBag.Message</p>

이제 애플리케이션 컨트롤러에서 다음과 같은 작업을 수행 할 수 있습니다.

public class WidgetsController : ControllerBase
{
  [HttpGet]
  public ActionResult Edit(int id)
  {
    Try
    {
       var widget = db.getWidgetById(id);
       if(widget == null)
          return ShowNotFound();
          //or return ShowNotFound("Invalid widget!");
       return View(widget);
    }
    catch(Exception ex)
    {
       //log error
       logger.Error(ex)
       return ShowServerError();
    }
  }
}

이제 경고를 위해 . 정적 파일 오류를 처리하지 않습니다. 따라서 example.com/widgets 와 같은 경로가 있고 사용자가 example.com/widgets.html로 변경 하면 IIS 기본 오류 페이지가 표시되므로 다른 방법으로 IIS 수준 오류를 처리해야합니다.


1

내 의견이 너무 길어서 답변을 게시하는 중 ...

유니콘 포스트 / 답변에 대한 의견과 질문입니다.

https://stackoverflow.com/a/7499406/687549

나는이 대답이 다른 사람들보다 간단하다는 점과 Microsoft의 일부 사람들이 분명히 상담했다는 사실을 선호합니다. 그러나 세 가지 질문이 있으며 응답 할 수 있으면 ASP.NET MVC (x) 앱의 웹에서 404/500 오류 답변의 성배라고 부릅니다.

@ Pure.Krome

  1. 당신이 코멘트에서 SEO 물건과 답변을 업데이트 할 수 GWB에 의해 지적 (이 답변이의 대한 언급 결코) - <customErrors mode="On" redirectMode="ResponseRewrite"><httpErrors errorMode="Custom" existingResponse="Replace">?

  2. 그런 식으로 그것을 할 괜찮 경우 당신은 당신의 ASP.NET 팀 친구를 요구하지 수 - 좋은 것 몇 가지 확인을 가지고 - 어쩌면 큰 변화에 더 노 없습니다 redirectModeexistingResponseSEO 멋지게 연주 할 수 있도록 이런 식으로?!

  3. 당신은 모든 물건을 둘러싼 몇 가지 설명을 추가 할 수 있습니다 ( customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", REMOVE customErrors누군가가 제안 완전하게 같은) 마이크로 소프트 친구에게 이야기 한 후?

내가 말하고 있었을 때; 54 000+ 조회수를 가진 상당히 인기있는 질문 인 것처럼 답변을보다 완벽하게 만들 수 있다면 좋을 것입니다.

업데이트 : 유니콘 답변은 302 Found와 200 OK를 수행하며 경로를 사용하여 404 만 반환하도록 변경할 수 없습니다. MVC : ish가 아닌 실제 파일이어야합니다. 다른 솔루션으로 넘어갑니다. 이것이 지금까지 궁극적 인 MVC : ish 답변으로 보였기 때문에 너무 나쁩니다.


1

허먼 칸의 솔루션과 거의 동일한 솔루션을 작은 주름으로 추가하면 프로젝트에 사용할 수 있습니다.

사용자 정의 오류 제어기를 작성하십시오.

public class Error404Controller : BaseController
{
    [HttpGet]
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View("404");
    }
}

그런 다음 사용자 정의 컨트롤러 팩토리를 작성하십시오.

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        return controllerType == null ? new Error404Controller() : base.GetControllerInstance(requestContext, controllerType);
    }
}

마지막으로 사용자 정의 오류 컨트롤러에 재정의를 추가하십시오.

protected override void HandleUnknownAction(string actionName)
{
    var errorRoute = new RouteData();
    errorRoute.Values.Add("controller", "Error404");
    errorRoute.Values.Add("action", "PageNotFound");
    new Error404Controller().Execute(new RequestContext(HttpContext, errorRoute));
}

그리고 그게 다야. Web.config를 변경할 필요가 없습니다.


1

1) 추상 컨트롤러 클래스를 만듭니다.

public abstract class MyController:Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;
        return View("NotFound");
    }

    protected override void HandleUnknownAction(string actionName)
    {
        this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound");
    }
    protected override void OnAuthorization(AuthorizationContext filterContext) { }
}  

2) 모든 컨트롤러 에서이 추상 클래스에서 상속하십시오.

public class HomeController : MyController
{}  

3) 그리고 View-Shared 폴더에 "NotFound"라는보기를 추가하십시오.


0

이 스레드에 게시 된 대부분의 솔루션을 살펴 보았습니다. 이 질문은 오래되었지만 지금도 여전히 새로운 프로젝트에 적용 가능하므로 여기에 제시된 답변뿐만 아니라 다른 곳에서 많은 시간을 보냈습니다.

@Marco가 404가 발생할 수있는 다른 사례를 지적했듯이, 나는 그 목록에 대해 함께 컴파일 한 솔루션을 확인했습니다. 그의 요구 사항 목록 외에도 하나 더 추가했습니다.

  • 솔루션은 MVC 및 AJAX / WebAPI 호출을 가장 적절한 방식으로 처리 할 수 ​​있어야합니다. (즉, MVC에서 404가 발생하면 Not Found 페이지를 표시해야하고 Web404에서 404가 발생하면 XML / JSON 응답을 가로 채서 사용하지 않는 Javascript가 쉽게 구문 분석 할 수 있도록해야합니다).

이 솔루션은 두 가지입니다.

그것의 첫 부분은 https://stackoverflow.com/a/27354140/2310818의 @Guillaume에서 온 것 입니다. 솔루션은 유효하지 않은 경로, 유효하지 않은 제어기 및 유효하지 않은 조치로 인해 발생한 404를 처리합니다.

WebForm을 만든 다음 MVC 오류 컨트롤러의 NotFound 작업을 호출하는 것이 좋습니다. 리디렉션 없이이 모든 작업을 수행하므로 Fiddler에서 단일 302가 표시되지 않습니다. 원래 URL도 유지되므로이 솔루션이 환상적입니다!


그것의 두 번째 부분은 https://stackoverflow.com/a/5536676/2310818의 @ Germán에서 온 것 입니다. 그들의 솔루션은 HttpNotFoundResult () 형태로 당신의 액션에 의해 반환 된 404를 처리하거나 새로운 HttpException ()을 던집니다!

아이디어는 MVC 컨트롤러에서 발생한 예외와 응답을 필터로보고 오류 컨트롤러에서 적절한 조치를 호출하는 것입니다. 다시이 솔루션은 리디렉션없이 작동하며 원래 URL은 유지됩니다!


보시다시피,이 두 솔루션은 모두 매우 강력한 오류 처리 메커니즘을 제공하며 @Marco에 나열된 모든 요구 사항과 내 요구 사항을 모두 충족합니다. 작동하는 샘플 또는이 솔루션의 데모를보고 싶다면 의견을 남겨 주시면 좋겠습니다.


0

나는 모든 기사를 살펴 보았지만 아무것도 효과가 없다.

 <system.web>
    <customErrors mode="On" redirectMode="ResponseRewrite">
      <error statusCode="404" redirect="~/PageNotFound.aspx"/>
    </customErrors>
  </system.web>
<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/PageNotFound.html" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

이 기사가 매우 유용하다는 것을 알았습니다. 한 번에 읽어야합니다. 손님 오류 페이지 벤 포스터

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