web-dev-qa-db-ja.com

ASP.NET MVCで404を正しく処理する方法を教えてください。

私は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"がない場合、これを要求するとWebサーバーから一般的な404ページが取得され、routing + overrideを使用する場合は404ページが表示されません。

それでは最後に、私の質問は次のとおりです。MVCフレームワーク自体の中で、経路などを使ってこのタイプの要求を捉える方法はありますか?

または私の404ハンドラとしてデフォルトでWeb.Config customErrorsを使用することをデフォルトにして、これをすべて忘れるべきですか? customErrorsを使用する場合は、直接アクセスに対するWeb.Configの制限により、/ Viewsの外部に一般的な404ページを格納する必要があります。

422
Brian

コードは 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));
}
262
Shay Jacoby

404の要件

以下は404ソリューションの私の要件であり、以下私はそれを実装する方法を示します:

  • 一致した経路を悪い行動で扱いたい
  • 一致しないルートを悪いコントローラで扱いたい
  • 不一致の経路(私のアプリでは理解できない任意のURL)を処理したい - これらのGlobal.asaxやIISまでバブリングしたくない 私はできる) t私のMVCアプリに正しくリダイレ​​クトします
  • 存在しない(おそらく削除された)オブジェクトに対してIDが送信された場合のように、上記と同じ方法でカスタム404を処理する方法が必要です。
  • 私は必要に応じて私は後でもっとデータを送ることができるMVCビュー(静的ページではない)を私の404すべてに返してほしい( 404良いデザインそして彼ら必須 HTTP 404ステータスコードを返す

溶液

未処理の例外やログ記録( Shay Jacoby's answer が示すように)のようなより高いことには、Application_ErrorをGlobal.asaxに保存するべきですが、404処理はしないでください。これが私の提案がGlobal.asaxファイルから404のものを締め出す理由です。

ステップ1:404エラーロジックの共通箇所

これは保守容易性のための良い考えです。 ErrorController を使用すると、将来的に よく設計された404ページ を容易に調整できるようになります。また、あなたの応答が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は、さまざまな場所で捕捉される必要があります。 1つ目はHandleUnknownActionです。

InvokeHttp404メソッドはErrorControllerと新しいHttp404アクションへの再ルーティングのための共通の場所を作成します。と思う DRY

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:コントローラファクトリで依存性注入を使用して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をキャッチする2番目の場所です。

ステップ4:アプリに解析されないURLに対して、Global.asaxにNotFoundルートを追加する

このルートはHttp404アクションを指しているはずです。ルーティングエンジンがここでドメイン部分を削除しているため、urlパラメータが相対URLになることに注意してください。そのため、ステップ1ですべての条件付きURLロジックを使用できます。

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

これはMVCアプリで404をキャッチするための3番目で最後の場所です。あなたがここで比類のないルートを見つけられない場合、MVCはASP.NET(Global.asax)に問題を渡します、そしてあなたは本当にこの状況でそれを望みません。

ステップ5:最後に、アプリが何かを見つけられないときに404を呼び出します

悪いIDが私のLoansコントローラに送信されたときのように(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);
    }

これらすべてをより少ないコードでより少ない場所に接続することができればいいのですが、この解決策はより保守可能で、よりテスト可能で、かなり実用的だと思います。

これまでのご意見ありがとうございます。もっと手に入れたいです。

注:これは私の最初の答えから大幅に編集されていますが、目的/要件は同じです - これが私が新しい答えを追加していない理由です

252
Matt Kocaj

ASP.NET MVCはカスタム404ページをあまりサポートしていません。カスタムコントローラファクトリ、キャッチオールルート、HandleUnknownActionname__付きのベースコントローラクラス

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

サンプルプロジェクト

232
Pavel Chuchuva

クイックアンサー/ TL; DR

enter image description here

怠惰な人々のために:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

それからglobal.asaxからこの行を削除します

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

これはIIS 7 +およびIIS Express専用です

Cassiniを使用しているのであれば、まあまあまあまあまあまあです… awkward


長い、説明された答え

私はこれが答えられたことを知っています。しかし、答えは本当に単純です(本当にこれに答えるために David Fowler and Damian Edwards に応援します)。

何もする必要はありません

ASP.NET MVC3の場合、すべてのビットと部分はそこにあります。

ステップ1 - > 2か所で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 Internal Server Errorです。

<system.web>の最初のセクションにカスタムエントリが1つしかないことを確認してください。 statusCode="404"エントリー? 500 Server Errorを含む他のすべてのエラー(つまり、コードにバグがあり、ユーザーの要求をクラッシュさせたときに発生する厄介なエラー)..その他のすべてのエラーは設定defaultRedirect="/ServerError"で処理されるため、ステータスコードは1つだけです。 404ページが見つからない場合は、ルート/ServerErrorに進んでください。

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

それは2つの無視ルート - > axd'sfaviconsをリ​​ストします(あなたのために、ooo!ボーナスはルートを無視します!)それから私は2つの明白なエラー処理ルートを持っています。この場合、デフォルトのものです。もちろん、私にはもっとありますが、それは私のWebサイトにとって特別なことです。 エラールートがリストの一番上にあることを確認してください。順序は必須です

最後に、global.asaxファイルの中にいる間は、HandleError属性をグローバルに登録しません。いいえ、いいえ、いいえ先生。ナッダいや。ニエン。負。 Noooooooooo ...

global.asaxからこの行を削除

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

ステップ3 - アクションメソッドを使ってコントローラを作成する

それでは、2つのアクションメソッドを持つコントローラを追加します。

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

わかりました、これをチェックしましょう。まず、NO[HandleError]属性があります。どうして?組み込みのASP.NETフレームワークはすでにエラーを処理しているので、エラーを処理するために必要な作業はすべて指定しています。

次に、2つのアクション方法があります。難しいことは何もありません。例外情報を表示したい場合は、Server.GetLastError()を使用してその情報を取得できます。

ボーナスWTF:はい、私はエラー処理をテストするために、3番目のアクションメソッドを作りました。

ステップ4 - ビューを作成する

そして最後に、2つのビューを作成します。このコントローラーのために、emを通常のビュースポットに配置します。

enter image description here

ボーナスコメント

  • Application_Error(object sender, EventArgs e)は必要ありません
  • 上記のステップはすべて Elmah で100%完璧に機能します。 Elmahの詐欺詐欺!

そしてそれは、私の友人たちもそれであるべきです。

さて、これを読んでおめでとうとユニコーンを賞品にしましょう!

enter image description here

153
Pure.Krome

私はA LOT MVCで404を適切に管理する方法(特にMVC3)について調べました、そして、これが、私がやってくる最良の解決策です。とアップ:

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

(オプション)

説明:

ご参考までに、ASP.NET MVC3アプリが404を生成することができる6つの異なるケースがあります。

(ASP.NET Frameworkによって自動的に生成されます。)

(1) URLがルートテーブルに一致しません。

(ASP.NET MVCフレームワークによって自動的に生成されます。)

(2)ルートテーブル内で一致するものが見つかりましたが、存在しないコントローラを指定しています。

(3)ルートテーブル内で一致するものが見つかりましたが、存在しないアクションを指定しています。

(手動で生成された:)

(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)に従わない。手動でプログラムする必要があります。

(3)

<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を見てください )。しかし、以前の解決策は、外部ライブラリを使用するという複雑さなしに、すべてのケースをカバーするようです。

84
Marco

私は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とコントローラが例外ルーチンを2回トリガするのを防ぎます。例えばasdfsdf/dfgdfgdのようなURLの場合

13
Dave Lowe

@ 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を使用していることに言及する必要があります。

6
Dave K

これはMVCツールを使用したもう1つの方法です。これは、不正なコントローラ名、不正なルート名、およびActionメソッド内に収まると思われるその他の基準に対する要求を処理できます。個人的には、302/200リダイレクトを行い、Razorビューを使用したResponseRewrite(Server.Transfer)をサポートしていないため、できるだけ多くの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

GlobalFilters.FiltersのGlobalFilterCollection(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

基本コントローラクラスを作成し、それをコントローラ内で継承します。このステップにより、未知のアクション名を処理してHTTP 404エラーをHandleHttpErrorAttributeに上げることができます。

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のスクリーンショットを追加して、one =を投稿できます。このソリューションを使用して次のURLにアクセスすると、Fiddlerは404メッセージを返します。

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

上記のcottsakの投稿とこれらの記事は参考になりました。

4
sky-dev

未処理の領域、コントローラ、およびアクションを扱う私の短縮ソリューション。

  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));
    
4
Herman Kan

MVC 4では、WebAPI 404は次のように処理できます。

コースエイピココントローラー

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

結果

enter image description here

3
Diganta Kumar

NotFoundMVCを試してみてください。それは動作します、セットアップなし。

2
DarthVader

標準的なCustomErrorsの設定うまくいくはずですしかし、Server.Transferに依存しているため、ResponseRewriteの内部実装はMVCと互換性がないようです。

これは私にとっては明白な機能上の穴のように感じられるので、私は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開発サーバー、Cassini)
  • クラシックモードのMVC4(IIS 6)

メリット

  • どの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>

あなたが注意を向けているのなら、Integrated Pipelineモードでは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
Red Taz

私の解決策は、誰かがそれを役に立つと思った場合に備えて。

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

SharedフォルダーにPageNotFound.cshtmlを追加してください。

2
Konamiman

ASP.NET MVCのエラーに対処することは、単に悩みの種です。私はこのページや他の質問やサイトでたくさんの提案を試みましたが、何もうまくいきません。 1つの提案はweb.config inside system.webserverでエラーを処理することでしたが、それは空白ページを返すだけです。

この解決策を思いつくときの私の目標は、

  • リダイレクトされない
  • デフォルトのエラー処理のように、正しいステータスコード200/OKを返さない

これが私の解決策です。

1system.web sectionに以下を追加

   <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と未処理の例外、特にビューで発生したものを処理します。私がaspx not htmlを使っていないことに注意してください。これは、背後のコードにレスポンスコードを追加できるようにするためです。

2。プロジェクトのルートにErrorという名前のフォルダーを作成し、2つのWebフォームを追加します。以下が私の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ページに同じことをする

。コントローラ内のエラーを処理します。やり方はたくさんあります。これは私のために働いたものです。私のコントローラーはすべて基本コントローラーから継承します。ベースコントローラには、以下のメソッドがあります。

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をShared viewsフォルダに追加してください。以下が私のものです。

<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レベルのエラーを処理する必要があります。

2
Moses Machua

私のコメントが長すぎたので答えを投稿しています...

Unicornの投稿/回答に対するコメントと質問の両方です。

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

それが単純であること、そしてどうやらマイクロソフトの何人かの人々が相談されたという事実のために、私は他のものよりこの答えを好む。私は3つの質問を受けました、そしてそれらが答えられることができるならば、私はこの答えをASP.NET MVC(x)アプリのためのinterwebsの上のすべての404/500エラー答えの聖杯と呼びます。

@ Pure.Krome

  1. GWBによって指摘されたコメントからSEOのものであなたの答えを更新することができますか(あなたの答えの中でこれについて言及することは決してありませんでした) - <customErrors mode="On" redirectMode="ResponseRewrite"><httpErrors errorMode="Custom" existingResponse="Replace">

  2. そのようにしても大丈夫かどうか、ASP.NETチームの友人に尋ねてもらえますか - 確認を得られればいいのですが - SEOでうまく遊べるようにするためにredirectModeexistingResponseを変更するのは大したことではありません!

  3. あなたがマイクロソフトで友達と話した後に、あなたがそのすべてのもの(誰かが示唆するようにcustomErrors redirectMode="ResponseRewrite"customErrors redirectMode="ResponseRedirect"httpErrors errorMode="Custom" existingResponse="Replace"、REMOVE customErrors COMPLETELY)を取り巻く明確化を加えることができますか?

私が言っていたように;これは54 000件以上のビューを含む非常に人気のある質問のようであるため、我々はあなたの答えをより完全にすることができればそれはすばらしいでしょう。

更新:Unicornの回答は302 Foundおよび200 OKであり、ルートを使用して404のみを返すように変更することはできません。それはあまりMVCではない物理ファイルでなければなりません。だから別の解決策に移ってください。これは究極のMVCであると思われるため、残念です。

1
PussInBoots

Herman Kanのものとほとんど同じですが、私のプロジェクトでうまくいくように小さなしわを付けて私のソリューションを追加します。

カスタムエラーコントローラを作成します。

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
Rob Lyndon

1)抽象Controllerクラスを作ります。

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"という名前のビューを追加します。

1
Mehmet

私はこのスレッドに投稿された解決策のほとんどを通過しました。この質問は古くなっているかもしれませんが、今でもまだ新しいプロジェクトに非常に当てはまります。そのため、ここで提示されている回答やその他の箇所についてはかなりの時間をかけて読んでいます。

@Marcoが404が発生する可能性があるさまざまなケースを指摘したので、私はそのリストに対してまとめたソリューションをチェックしました。彼の要件リストに加えて、もう1つ追加しました。

  • このソリューションは、AJAX/WebAPI呼び出しと同様にMVCも最も適切な方法で処理できるはずです。 (つまり、404がMVCで発生した場合は[見つかりません]ページが表示され、404がWebAPIで発生した場合は、XML/JSON応答がハイジャックされないようにしてください。

この解決策は2倍です。

その最初の部分は@Guillaumeから来ています https://stackoverflow.com/a/27354140/2310818 。彼らの解決策は無効な経路、無効なコントローラと無効なアクションが原因で発生したあらゆる404の世話をします。

そのアイデアは、Webフォームを作成してから、それをMVC Errors ControllerのNotFoundアクションと呼ばせることです。あなたはFiddlerに一つの302を見ることはないでしょうから、それはいかなるリダイレクトもなしにこれの全てをします。元のURLも保持されるため、このソリューションは素晴らしいものになります。


その2番目の部分は@Germánから来ています https://stackoverflow.com/a/5536676/2310818 。彼らの解決策は、HttpNotFoundResult()の形であなたのアクションによって返される404を処理するか、または新しいHttpException()をスローします!

そのアイデアは、MVCコントローラーによってスローされたレスポンスと例外をフィルターで調べて、Errors Controllerで適切なアクションを呼び出すことです。このソリューションもリダイレクトなしで機能し、元のURLは保持されます。


ご覧のとおり、これらのソリューションは両方とも非常に堅牢なエラー処理メカニズムを提供し、@ Marcoにリストされているすべての要件と私の要件を満たしています。このサンプルの実用的なサンプルやデモをご覧になりたい場合は、コメント欄に記入してください。

0
Parth Shah

すべての記事を読み終えましたが、私には何も機能しません。私の要件ユーザーには、URLのカスタム404ページに何かを入力してください。404の処理を正しく理解する必要があります。

 <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>

私はこの記事が非常に参考になったことがわかりました。すぐに読むべきです。 カスタマーエラーページ - Ben Foster

0
Thakur Rock