443

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

ただし、以前の戦略では、不良/不明なコントローラーへの要求は処理されません。たとえば、「/ IDoNotExist」がありません。これをリクエストすると、ルーティング+オーバーライドを使用する場合は404ではなく、Webサーバーから汎用の404ページを取得します。

最後に、私の質問は 、MVCフレームワーク自体でルートまたは他の何かを使用してこのタイプのリクエストをキャッチする方法はありますか?

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

4

19 に答える 19

278

コードは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));
}
于 2009-03-06T21:49:26.890 に答える
258

404 の要件

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

  • 一致したルートを悪いアクションで処理したい
  • 一致したルートを不正なコントローラーで処理したい
  • 一致しないルート (アプリが理解できない任意の URL) を処理したい - これらを Global.asax または IIS にバブリングさせたくありません。MVCアプリに適切にリダイレクトできないためです。
  • 上記と同じように、カスタム 404 を処理する方法が必要です。たとえば、存在しない (おそらく削除された) オブジェクトの ID が送信された場合などです。
  • すべての 404 が MVC ビュー (静的ページではない) を返し、必要に応じて後でより多くのデータを送り込むことができるようにしたい (良い 404 設計) それらは HTTP 404 ステータス コードを返す必要があります。

解決

Application_Error未処理の例外やログ ( Shay Jacoby の回答が示すように) など、より高度なものについては Global.asax に保存する必要があると思いますが、404 処理は保存しないでください。これが、私の提案が 404 を Global.asax ファイルから除外する理由です。

ステップ 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: ベース コントローラ クラスを使用して、カスタム 404 アクションを簡単に呼び出して接続できるようにするHandleUnknownAction

ASP.NET MVC の 404 は、さまざまな場所でキャッチする必要があります。最初はHandleUnknownAction

このメソッドは、新しいアクションInvokeHttp404に再ルーティングするための共通の場所を作成します。DRYを考えてください!ErrorControllerHttp404

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 をキャッチする 2 番目の場所です。

ステップ 4: アプリへの解析に失敗した URL の Global.asax に NotFound ルートを追加する

Http404このルートは、私たちのアクションを指している必要があります。urlルーティング エンジンがここでドメイン部分を削除しているため、param が相対 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);
    }

これらすべてをより少ないコードでより少ない場所に接続できればいいのですが、このソリューションはより保守しやすく、テストしやすく、かなり実用的だと思います。

これまでのフィードバックに感謝します。もっと手に入れたいです。

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

于 2010-04-05T05:46:08.477 に答える
241

ASP.NET MVC は、カスタム 404 ページをうまくサポートしていません。カスタム コントローラー ファクトリ、キャッチオール ルート、ベース コントローラー クラスHandleUnknownAction- えーっと!

これまでのところ、IIS カスタム エラー ページの方が優れた代替手段です。

web.config

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

エラーコントローラー

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

サンプルプロジェクト

于 2011-03-31T22:27:46.377 に答える
154

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

ここに画像の説明を入力

そこにいる怠け者のために:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

次に、この行を削除しますglobal.asax

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

これは、IIS7+ および IIS Express のみに適用されます。

Cassini を使用している場合は、まあ .. うーん .. えーと .. ぎこちなく... 気まずい


長く説明された答え

私はこれが答えられたことを知っています。しかし、答えは本当にシンプルです (これに本当に答えてくれたDavid FowlerDamian Edwardsに乾杯します)。

custom を行う必要はありません

について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>

ここで、使用することにした ROUTES に注意してください。何でも使用できますが、私のルートは

  • /NotFound<- 404 が見つからない、エラー ページの場合。
  • /ServerError<- その他のエラーについては、私のコードで発生したエラーを含めてください。これは 500 内部サーバー エラーです

の最初のセクションにカスタム エントリが1 つ<system.web>しかないことがわかりますか? エントリー?(つまり、コードにバグがあり、ユーザーの要求をクラッシュさせたときに発生する厄介なエラー) を含む他のすべてのエラーがあるため、1 つのステータス コードのみをリストしました。他のすべてのエラーは設定によって処理されます。 、404ページが見つからない場合は、ルートに移動してください。statusCode="404"500 Server ErrordefaultRedirect="/ServerError"/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'sそしてfavicons(おまけにルートを無視します、あなたのために!)そして(そして順序はここで重要です)、私は2つの明示的なエラー処理ルートを持っています..その後に他のルートが続きます。この場合、デフォルトのものです。もちろん、他にもありますが、それは私の Web サイトにとって特別なものです。エラールートがリストの一番上にあることを確認してください。注文は必須です。

最後に、global.asaxファイル内では、HandleError 属性をグローバルに登録しません。いいえ、いいえ、いいえ。なだ。いいえ。にえん。ネガティブ。いやぁぁぁぁぁぁぁ...

からこの行を削除します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");
    }
}

わかりました、これをチェックしましょう。まず、ここには属性 [HandleError]がありません。なんで?組み込みのASP.NETフレームワークはすでにエラーを処理しており、エラーを処理するために必要なすべてのたわごとを指定しているため:) それはこのメソッドにあります!

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

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

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

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

ここに画像の説明を入力

ボーナスコメント

  • あなたは必要ありませんApplication_Error(object sender, EventArgs e)
  • 上記の手順はすべて、Elmahで 100% 完全に機能します。エルマー フレッキング ロウクス!

そして、それは私の友人たちです。

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

ここに画像の説明を入力

于 2011-09-21T12:02:34.550 に答える
90

MVC (特に MVC3)404 を適切に管理する方法について多くのことを調査しましたが、これは私が思いついた最良のソリューションです。

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

エラーコントローラー:

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) 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) に準拠しません。手動でプログラムする必要があります。

(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を参照)。しかし、以前のソリューションは、外部ライブラリを使用する複雑さなしに、すべてのケースをカバーしているようです。

于 2012-01-26T23:20:26.790 に答える
14

私は 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 の場合

于 2010-05-29T20:50:45.297 に答える
7

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

私はMVC2.0を使用していることに言及する必要があります。

于 2010-07-29T23:43:14.003 に答える
5

これは、MVC ツールを使用して、不適切なコントローラー名、不適切なルート名、およびアクション メソッド内に適合するその他の基準への要求を処理できる別の方法です。Server.Transfer個人的には、302 / 200 リダイレクトを行い、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

基本コントローラー クラスを作成し、コントローラーでそれを継承します。この手順により、不明なアクション名を処理し、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 のスクリーンショットを追加します (=) を投稿するのに十分な投票が得られた場合。このソリューションを使用して次の URL にアクセスすると、Fiddler は 404 メッセージを返します。

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

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

于 2011-11-01T14:11:52.403 に答える
4

MVC4では、WebAPI404は次のように処理できます。

コース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 }
    );
}

結果

ここに画像の説明を入力してください

于 2012-05-08T12:59:48.147 に答える
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();
    }
}

フォルダーに aPageNotFound.cshtmlを追加するだけです。Shared

于 2013-10-01T10:31:13.600 に答える
2

ただし、標準CustomErrors構成は機能するはずですが、依存しServer.Transferているため、の内部実装はResponseRewriteMVC と互換性がないようです。

これは明らかな機能の穴のように感じられるので、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 (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() {

        }


    }

}

使用法

これを最終的な HTTP モジュールとして web.config に含めます。

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

}
于 2015-03-31T09:37:27.643 に答える
2

nuget で NotFoundMVC を試してください。それは動作します、セットアップはありません。

于 2013-07-09T14:06:53.140 に答える
1

コメントが長すぎたので回答を投稿します...

これは、ユニコーンの投稿/回答へのコメントと質問の両方です。

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

シンプルであり、明らかにMicrosoftの何人かの人々が相談されたという事実のために、私は他の回答よりもこの回答を好みます。私は 3 つの質問を受け取りましたが、それらに答えることができれば、この答えを、ASP.NET MVC (x) アプリのインターウェブ上のすべての 404/500 エラーの答えの聖杯と呼びます。

@ピュアクローム

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

  2. ASP.NET チームの友人に、そのようにしてよいかどうか尋ねていただけますか?確認していただければ幸いです。おそらく、変更することは大したことではなくredirectModeexistingResponseこの方法で SEO とうまく連携できるようになるのではないでしょうか?!

  3. customErrors redirectMode="ResponseRewrite"Microsoft の友人と話した後、これらすべて (誰かが提案したようcustomErrors redirectMode="ResponseRedirect"に、完全httpErrors errorMode="Custom" existingResponse="Replace"に削除) について説明を追加できますか?customErrors

私が言っていたように; これは 54,000 回以上の閲覧数を持つかなり人気のある質問であるように思われるため、回答をより完全なものにすることができれば素晴らしいことです。

更新: Unicorn の回答は 302 Found と 200 OK を実行し、ルートを使用して 404 のみを返すように変更することはできません。MVC らしくない物理ファイルでなければなりません。したがって、別のソリューションに進みます。これが究極のMVCのように見えたので、残念です。これまでの回答です。

于 2014-01-14T14:36:47.353 に答える