web-dev-qa-db-ja.com

.NET MVC 2で適切なHTTPエラー処理を実装する方法は?

ASP.NET MVC2アプリにエラー処理を実装するのに一日中苦労しています。さまざまなテクニックを見てきましたが、どれも正しく機能しません。私はMVC2と.NET 4.0を使用しています(MVC3がリリースされる前にプロジェクトを開始しました。最初のリリースを提供した後でアップグレードします)。

この時点で、404エラーと500エラーを適切に処理できるようになりました。403(承認が必要)も素晴らしいでしょう。その後、他のさまざまな特定の応答が続きます。現在、すべての404、すべての500、すべての302を404の前に取得するか、すべての302を500の前に取得しています。

ここに私の要件があります(これはHTTPの基本的な要件にかなり近いはずです):

  • リソースが見つからない場合は、404をスローし、要求されたURLを含む404固有のページを表示します。 302のような中間応答コードを返さないでください。理想的には、/Error/NotFoundのような新しいURLを表示するのではなく、要求されたURLを保持します。ただし、後者が表示された場合は、リダイレクト応答を返さないようにしてください。 。

  • 内部サーバーエラーが発生した場合は、500をスローし、500固有のエラーを表示して、問題の原因を示します。繰り返しになりますが、中間応答コードを返さないでください。理想的には、URLを変更しないでください。

これが私が404と考えるものです:

  1. 静的ファイルが見つかりません:/Content/non-existent-dir/non-existent-file.txt
  2. コントローラーが見つかりません:/non-existent-controller/Foo/666
  3. コントローラは見つかりましたが、アクションが見つかりません:/Home/non-existent-action/666
  4. コントローラとアクションは見つかりましたが、アクションはリクエストされたオブジェクトを見つけることができません:/Home/Login/non-existent-id

これが私が500と考えるものです:

  1. 悪い値を投稿してください:POST /User/New/new-user-name-too-long-for-db-column-constraint
  2. Webサービスエンドポイントが応答しないなど、データに関連しない問題

これらの問題のいくつかは、特定のコントローラーまたはモデルによって識別される必要があり、その後、コントローラーは適切なHttpExceptionをスローする必要があります。残りはより一般的に処理する必要があります。

404ケース#2の場合、コントローラーが見つからない場合は、カスタムControllerFactoryを使用して404をスローしようとしました。 404ケース#3の場合、カスタムベースコントローラーを使用してHandleUnknownActionをオーバーライドし、404をスローしようとしました。

どちらの場合も、404の前に302が表示されます。また、500エラーが発生することはありません。 Web.configを変更してWebサービスエンドポイントにタイプミスを入れても、302が表示され、次に404が使用するURL(コントローラー/アクション)を示しますWebサービスが見つかりません。また、要求されたURLを(n個の不要な)クエリ文字列パラメーターとして取得します:/Error/NotFound?aspxerrorpath=/Home/non-existent-action

これらの手法は両方とも http://www.niksmit.com/wp/?p=17 (ASP.Net MVCを使用して通常の404(ページが見つかりません)エラーページを取得する方法)から指摘されましたから http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/

Web.configに<customErrors mode="On" defaultRedirect="~/Error/Unknown" redirectMode="ResponseRedirect" />がある場合、適切な応答コードを取得しますが、エラーコントローラーが呼び出されることはありません。 redirectMode属性を取得すると、MVCエラービューが表示されますが、302と変更されたURLがあり、常に同じコントローラーです(Unknown = 500; NotFoundに変更すると、すべてが404のようになります)。

ここに私が読んで実装しようとした他のいくつかのものがあります:

..StackOverflowの投稿の束と一緒に。

私にはこの種のエラー処理はWebアプリにとってかなり基本的なものであり、MVCフレームワークには、デフォルトでこれを実行するデフォルトがあり、他の人が機能するように拡張できるようにすべきです。おそらく彼らは将来のリリースでそれを行うでしょう。それまでの間、適切なHTTP応答を実装する方法の包括的な詳細を誰かに教えてもらえますか?

39
Val

これがあなたが使うことができる1つのテクニックです。エラーページを提供するErrorsControllerを定義します。

public class ErrorsController : Controller
{
    public ActionResult Http404()
    {
        Response.StatusCode = 404;
        return Content("404", "text/plain");
    }

    public ActionResult Http500()
    {
        Response.StatusCode = 500;
        return Content("500", "text/plain");
    }

    public ActionResult Http403()
    {
        Response.StatusCode = 403;
        return Content("403", "text/plain");
    }
}

次に、Global.asaxApplication_Errorイベントをサブスクライブして、例外をログに記録し、ErrorsControllerの対応するアクションを実行できます。

protected void Application_Error(object sender, EventArgs e)
{
    var app = (MvcApplication)sender;
    var context = app.Context;
    var ex = app.Server.GetLastError();
    context.Response.Clear();
    context.ClearError();
    var httpException = ex as HttpException;

    var routeData = new RouteData();
    routeData.Values["controller"] = "errors";
    routeData.Values["exception"] = ex;
    routeData.Values["action"] = "http500";
    if (httpException != null)
    {
        switch (httpException.GetHttpCode())
        {
            case 404:
                routeData.Values["action"] = "http404";
                break;
            case 403:
                routeData.Values["action"] = "http403";
                break;
            case 500:
                routeData.Values["action"] = "http500";
                break;
        }
    }
    IController controller = new ErrorsController();
    controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData));
}

そして今、残っているのは適切な例外を投げ始めることです:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        throw new HttpException(404, "NotFound");
    }
}
49
Darin Dimitrov

HTTP 404エラー(リダイレクトなし)については、この件に関する私のブログ投稿をご覧ください。これはあなたにいくつかの良いアイデアを与えるかもしれません:

http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16

4
Hector Correa

これは非常に古い質問です。しかし、親愛なる "Jesse Webbの答え" で見たHttp例外を処理するためのはるかにクリーンな方法を紹介することは価値があると思いました。

解決策は、system.webServerセクションのhttpErrors要素を使用することです。

<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

この方法ですべての例外をログに記録することもできます。 " " Jesse Webb's answer " "をお読みください。

これは本当にすっきりしていて、他のすべてのソリューションと同様に機能します(リダイレクトなし)。

:これはIIS 7以降でのみ機能します。(httpErrorsのため)最近追加された要素。

0

これはあなたの質問に答えませんが、HTTPステータス500はサーバーで問題が発生したことを示していることに注意することが重要です。したがって、あなたの例は次のとおりです。

POST /User/New/new-user-name-too-long-for-db-column-constraint

500をスローする正当な理由ではなく、そのデータ検証の問題であり、MVCデータ注釈またはjQuery検証フレームワークなどで処理する必要があります。TextBoxの横に「ユーザー名が長すぎます」というエラーメッセージを表示する方がはるかに優れています。

0
JK.