web-dev-qa-db-ja.com

ASP.NET MVC4 CustomErrorsDefaultRedirect無視

カスタムHandleErrorAttributeを使用してカスタム例外のみを処理するMVC4アプリがあります。デフォルトの404およびその他の500以外のエラーページをインターセプトして、より魅力的なものに置き換えたいと思います。そのために、Web.configに以下を追加しました。

<system.web>
    <customErrors mode="On" defaultRedirect="~/Error/Index" />
...
</ system.web>

Indexメソッドと対応するビューを備えたエラーコントローラーがありますが、それでもデフォルトの404エラーページが表示されます。また、defaultRedirectを静的なhtmlファイルに設定してみました。 <customErrors>内に404に固有のエラー処理を追加しようとしましたが、プログラムでルートを変更しようとしましたが、すべて結果がありませんでした。何が足りないのですか? ASPがデフォルトのエラー処理を無視するのはなぜですか?

注:<customErrors mode="On"を使用しても、CustomHandleErrorAttributeをローカルでテストできないことに以前気づきました。ただし、開発ボックスからサーバーにアクセスすると機能します...それが関連しているかどうかはわかりません。 この男 同じ問題がありました。

13

これはうまくいくはずです:

1。Web.Config

<customErrors mode="On"
   defaultRedirect="~/Views/Shared/Error.cshtml">

  <error statusCode="403"
    redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />

  <error statusCode="404"
    redirect="~/Views/Shared/FileNotFound.cshtml" />

</customErrors>

2。次のようにFilterConfigクラスにグローバルアクションフィルターとしてHandleErrorAttributeを登録しました

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }

それでもうまくいかない場合は、Global.asaxで次のようなステータスコードを確認して、応答を転送するようにしてください。少なくとも機能する必要があります。

void Application_EndRequest(object sender, EventArgs e)
{
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        Server.Transfer("~/Views/Shared/UnauthorizedAccess.cshtml");
    }
}
14
Nirmal Subedi

私は少し話題から外れます。これを説明するのは少し重要だと思いました。

enter image description here

上記の強調表示された部分に注意を払う場合。アクションフィルターの順序を指定しました。これは基本的にアクションフィルターの実行順序を説明します。これは、コントローラー/アクションメソッドに複数のアクションフィルターが実装されている場合の状況です。

enter image description here

この画像は、2つのアクションフィルターがあるとしましょう。 OnActionExecutionは優先度で実行を開始し、OnActionExecutedは下から上に開始します。つまり、OnActionExecutedの場合、最も高い順序のアクションフィルターが最初に実行され、OnActionExecutingの場合、最も低い順序のアクションフィルターが最初に実行されます。以下の例

public class Filter1 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

//実行はここから始まります-1

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {

//実行はここに移動します-5

        base.OnActionExecuted(filterContext);
    }
}

public class Filter2 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

//実行はここに移動します-2

        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {

//実行はここに移動します-4

        base.OnActionExecuted(filterContext);
    }
}

[HandleError]
public class HomeController : Controller
{
    [Filter1(Order = 1)]
    [Filter2(Order = 2)]
    public ActionResult Index()
    {

//実行はここに移動します-3

        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }
}

MVCフレームワーク内にはさまざまなタイプのフィルターがあることをすでにご存知かもしれません。それらは以下にリストされています。

  1. 承認フィルター

  2. アクションフィルター

  3. 応答/結果フィルター

  4. 例外フィルター

各フィルター内で、Orderプロパティを指定できます。これは基本的に、アクションフィルターの実行順序を説明します。

元のクエリに戻る

これは私にとってはうまくいきます。これは非常に簡単で、Web.Configの変更を検討したり、Global.asaxファイルにアクションフィルターを登録したりする必要はありません。

oK。したがって、最初に単純なアクションフィルターを作成しています。これは、Ajaxおよび非Ajaxリクエストを処理します。

public class MyCustomErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        filterContext.ExceptionHandled = true;
        var debugModeMsg = filterContext.HttpContext.IsDebuggingEnabled
                               ? filterContext.Exception.Message +
                                 "\n" +
                                 filterContext.Exception.StackTrace
                               : "Your error message";

//これは、Ajaxリクエストを処理する必要がある場合です

        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    error = true,
                    message = debugModeMsg
                }
            };
        }

//これは、Ajax以外のリクエストを処理する場合です

        else
        {
            var routeData = new RouteData();
            routeData.Values["controller"] = "Error";
            routeData.Values["action"] = "Error";
            routeData.DataTokens["area"] = "app";
            routeData.Values["exception"] = debugModeMsg;
            IController errorsController = new ErrorController();
            var exception = HttpContext.Current.Server.GetLastError();
            var httpException = exception as HttpException;
            if (httpException != null)
            {
                Response.StatusCode = httpException.GetHttpCode();
                switch (System.Web.HttpContext.Current.Response.StatusCode)
                {
                    case 404:
                        routeData.Values["action"] = "Http404";
                        break;
                }
            }

            var rc = new RequestContext
                         (
                             new HttpContextWrapper(HttpContext.Current),
                             routeData
                         );
            errorsController.Execute(rc);
        }
        base.OnException(filterContext);
    }
}

これで、このアクションフィルターをコントローラーとアクションのみに実装できます。例:

enter image description here

これがお役に立てば幸いです。

6
Imad Alazani

この問題を調査した後、私の知識を共有したいと思います。私の発言を改善するのに役立つコメントは大歓迎です。

ASP.NET MVCには、次の順序でHTTP要求を処理する3つのレイヤーがあります(応答は逆の順序で転送されます)。

  1. IIS(HTTPレイヤー)

  2. ASP.NET(サーバーレイヤー)

  3. コントローラー(MVCレイヤー)

これらのすべてのレイヤーにはエラー処理がありますが、各レイヤーの処理は異なります。 IISから始めます。

IISレイヤー

IISがエラーを処理する方法の最も簡単な例は、ブラウザーを使用して、サーバーから存在しない.htmlファイルを要求することです。アドレスは次のようになります。

http:// localhost:50123/this_does_not_exist.html

ブラウザタブのタイトルに注意してください。例:IIS 10.0詳細エラー-404.0-見つかりません

ASP.NETレイヤー

IISがHTTP要求を受信すると、URLが.aspxで終わる場合、この拡張子を処理するように登録されているため、ASP.NETに転送します。ASP.NETが処理する方法の最も簡単な例エラーは、ブラウザを使用して、サーバーに存在しない.aspxファイルを要求することです。アドレスは次のようになります。

http:// localhost:50123/this_does_not_exist.aspx

ページの下部に表示されるバージョン情報に注意してください。これは、ASP.NETのバージョンを示しています。

customErrorsタグは、もともとASP.NET用に作成されました。応答がASP.NET内部コードによって作成された場合にのみのみ効果があります。これは、アプリケーションコードから作成された応答には影響しないことを意味します。さらに、ASP.NETによって返される応答にコンテンツがなく、エラーステータスコード(4xxまたは5xx)がある場合、IISは、ステータスコードに従って応答を置き換えます。いくつかの例を提供します。

Page_Load メソッドにResponse.StatusCode = 404が含まれている場合、コンテンツは正常に表示されます。追加のコードResponse.SuppressContent = trueが追加されると、IISが介入し、「this_does_not_exist.html」を要求する場合と同じ方法で404エラーを処理します。コンテンツとステータスのないASP.NET応答コード2xxは影響を受けません。

ASP.NETがアプリケーションコードを使用して要求を完了できない場合、ASP.NETは内部コードを使用して要求を処理します。次の例を参照してください。

URLを解決できない場合、ASP.NETはそれ自体で応答を生成します。デフォルトでは、問題の詳細を含むHTML本文で404応答を作成します。代わりに、customErrorsを使用して302(リダイレクト)応答を作成できます。ただし、ASP.NETが404応答を返す原因となる有効なURLにアクセスしても、customErrorsで指定されたリダイレクトはトリガーされません。

ASP.NETがアプリケーションコードから例外をキャッチした場合も同じことが起こります。デフォルトでは、例外の原因となったソースコードに関する詳細を含むHTML本文で500応答を作成します。この場合も、customErrorsを使用して、代わりに302(リダイレクト)応答を生成できます。ただし、アプリケーションコードから500応答を作成しても、customErrorsで指定されたリダイレクトはトリガーされません。

defaultRedirectおよびerrorタグは、何を考慮して理解するのが非常に簡単です。私はちょうど言った。エラータグは、特定のステータスコードのリダイレクトを指定するために使用されます。対応するエラータグがない場合は、defaultRedirectが使用されます。リダイレクトURLは、コントローラーアクションを含め、サーバーが処理できるすべてのものを指すことができます。

MVCレイヤー

ASP.NET MVCを使用すると、事態はさらに複雑になります。まず、2つの「Web.config」ファイルがあります。1つはルートにあり、もう1つはViewsフォルダーにあります。ビューのデフォルトの「Web.config」は、このスレッドに対して2つの重要なことを実行することに注意してください。

  • .cshtmlファイルへのURLの処理を無効にします(webpages:Enabledをfalseに設定)
  • これは、Viewsフォルダー(BlockViewHandler)内のコンテンツへの直接アクセスを防ぎます。

ASP.NET MVCの場合、HandleErrorAttributeGlobalFilters に追加できます。これには、の値も考慮されます。ルート「Web.config」からのcustomErrorsタグのmode属性。具体的には、設定がオンの場合、コントローラー/アクションコードでキャッチされなかった例外のMVCレイヤーでのエラー処理が有効になります。それらをASP.NETに転送するのではなく、デフォルトでViews/Shared /Error.cshtmlをレンダリングします。これは、HandleErrorAttributeのViewプロパティを設定することで変更できます。

MVCレイヤーでのエラー処理は、リクエストURLに基​​づいて、コントローラー/アクションが解決された後に開始されます。たとえば、アクションのパラメータを満たさないリクエストは、MVCレイヤーで処理されます。ただし、POSTリクエストにPOSTを処理できる一致するコントローラー/アクションがない場合、エラーはASP.NETレイヤーで処理されます。

テストにはASP.NETMVC5を使用しました。エラー処理に関して、IISとIIS Expressの間に違いはないようです。

回答

500以外のステータスコードでcustomErrorsが考慮されない理由を考えることができる唯一の理由は、それらがHttpStatusCodeResponseで作成されているためです。この場合、応答はアプリケーションコードによって作成され、ASP.NETではなくIISによって処理されます。この時点で、代替ページを構成することは無意味です。この動作を再現するサンプルコードを次に示します。

public ActionResult Unhandled404Error()
{
    return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}

このようなシナリオでは、OnResultExecutedをオーバーライドして次のようなことを行う ActionFilterAttribute を実装することをお勧めします。

int statusCode = filterContext.HttpContext.Response.StatusCode;
if(statusCode >= 400)
{
    filterContext.HttpContext.Response.Clear();
    filterContext.HttpContext.Response.Redirect("/Home/Index");
}

実装されたActionFilterAttributeをGlobalFiltersに追加する必要があります。

1
MatrixRonny

この答えがあなたに役立つかどうかはわかりませんが、これは簡単な方法です...私はerror.htmlを/に配置し、Web構成のカスタムエラーに対してモードをオンにしました。これは完全に機能します...

  <system.web>
    <customErrors defaultRedirect="~/Error.html" mode="On" />
  </system.web>

このerror.htmlは、頭と体のある基本的なhtmlページです。

0
Venkata Tata

コントローラErrorControllerを作成します。

 public class ErrorController : Controller
    {
        //
        // GET: /Error/

        public ActionResult Index()
        {
            return View();
        }
}

アクションのインデックスビューを作成します。

web.configで

<customErrors mode="On">
      <error statusCode="404" redirect="Error/Index"/>
</customErrors>

コード/ロジックのエラーを処理しているとき

[HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "Modify this template to jump-start application.";

            return View("Index2");
        }
}

[HandleError]属性-共有フォルダー内のError.cshtmlページにリダイレクトされます。

0
chamara

私にとっては、デフォルトのError.cshtmlファイルを削除することで機能し、Web.configのカスタムErrordefaultRedirectページを取得しています。

0
Camilo Gómez