14

共有ホストにデプロイした ASP.NET MVC アプリでカスタム エラーの問題が発生しています。ErrorController を作成し、次のコードを Global.asax に追加して、未処理の例外をキャッチしてログに記録し、制御を ErrorController に移してカスタム エラーを表示します。このコードはここから取られます:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    Response.Clear();

    HttpException httpEx = ex as HttpException;
    RouteData routeData = new RouteData();
    routeData.Values.Add("controller", "Error");

    if (httpEx == null)
    {
        routeData.Values.Add("action", "Index");
    }
    else
    {
        switch (httpEx.GetHttpCode())
        {
            case 404:
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                routeData.Values.Add("action", "HttpError500");
                break;
            case 503:
                routeData.Values.Add("action", "HttpError503");
                break;
            default:
                routeData.Values.Add("action", "Index");
                break;
        }
    }

    ExceptionLogger.LogException(ex); // <- This is working. Errors get logged

    routeData.Values.Add("error", ex);
    Server.ClearError();
    IController controller = new ErrorController();
    // The next line doesn't seem to be working
    controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}

ロギングが正常に機能するため、Application_Error は間違いなく起動しますが、カスタム エラー ページを表示する代わりに、Go Daddy の一般的なエラー ページが表示されます。上記のコードを引用したブログ投稿のタイトルから、MVC フレームワークの Release Candidate 2 を使用していることがわかります。1.0 で、コードの最後の行が機能しないような変更がありましたか? いつものように、私のマシンではうまく機能します。

どんな提案でも大歓迎です。

編集: Web.config の customErrors モードの 3 つの可能性 (オフ、オン、およびリモートのみ) をすべて試したことを忘れていました。この設定に関係なく、同じ結果になります。

編集 2: また、Controller クラスの [HandleError] 装飾の有無にかかわらず試してみました。

更新: 404 を見つけて修正しました。Go Daddy の Hosting Control Center には、404 の動作を制御できる設定パネルのセクションがあり、デフォルトでは一般的なページが表示されます。これにより、Web.config 設定が上書きされるようです。これで、カスタム 404 ページが意図したとおりに表示されるようになりました。ただし、500 と 503 はまだ機能していません。Sql Server が次のように例外をスローした場合に、HomeController にコンテンツの静的テキスト バージョンを取得するコードがあります。

public ActionResult Index()
{
    CcmDataClassesDataContext dc = new CcmDataClassesDataContext();

    // This might generate an exception which will be handled in the OnException override
    HomeContent hc = dc.HomeContents.GetCurrentContent();

    ViewData["bodyId"] = "home";
    return View(hc);
}

protected override void OnException(ExceptionContext filterContext)
{
    // Only concerned here with SqlExceptions so an HTTP 503 message can
    // be displayed in the Home View. All others will bubble up to the
    // Global.asax.cs and be handled/logged there.
    System.Data.SqlClient.SqlException sqlEx =
        filterContext.Exception as System.Data.SqlClient.SqlException;
    if (sqlEx != null)
    {
        try
        {
            ExceptionLogger.LogException(sqlEx);
        }
        catch
        {
            // couldn't log exception, continue without crashing
        }

        ViewData["bodyId"] = "home";
        filterContext.ExceptionHandled = true;
        HomeContent hc = ContentHelper.GetStaticContent();
        if (hc == null)
        {
            // Couldn't get static content. Display friendly message on Home View.
            Response.StatusCode = 503;
            this.View("ContentError").ExecuteResult(this.ControllerContext);
        }
        else
        {
            // Pass the static content to the regular Home View
            this.View("Index", hc).ExecuteResult(this.ControllerContext);
        }
    }
}

静的コンテンツを取得しようとするコードは次のとおりです。

public static HomeContent GetStaticContent()
{
    HomeContent hc;

    try
    {
        string path = Configuration.CcmConfigSection.Config.Content.PathToStaticContent;
        string fileText = File.ReadAllText(path);
        string regex = @"^[^#]([^\r\n]*)";
        MatchCollection matches = Regex.Matches(fileText, regex, RegexOptions.Multiline);
        hc = new HomeContent
            {
                ID = Convert.ToInt32(matches[0].Value),
                Title = matches[1].Value,
                DateAdded = DateTime.Parse(matches[2].Value),
                Body = matches[3].Value,
                IsCurrent = true
            };
    }
    catch (Exception ex)
    {
        try
        {
            ExceptionLogger.LogException(ex);
        }
        catch
        {
            // couldn't log exception, continue without crashing
        }
        hc = null;
    }

    return hc;
}

接続文字列を変更して SqlException を生成すると、コードがエラーを適切にログに記録し、静的コンテンツを取得して表示することを確認しました。しかし、Web.config の静的テキスト ファイルへのパスも変更して 503 バージョンのホーム ビューをテストすると、代わりに「サービスを利用できません」以外の何もないページが表示されます。それでおしまい。サイトのルック アンド フィールを備えたカスタム 503 メッセージはありません。

役立つコードの改善に関する提案はありますか? HttpResponse に別のヘッダーを追加すると役に立ちますか? それとも、ゴー・ダディが 503 を強引に乗っ取っているのでしょうか?

4

2 に答える 2

29

私は解決策を見つけましたが、それは信じられないほど簡単です。問題は実際には IIS7 にあったことが判明しました。Visual Studio でこの問題をデバッグしているときに、以前は気付かなかった HttpResponse オブジェクトのプロパティを見つけました。

public bool TrySkipIisCustomErrors { get; set; }

これにより、最も近い検索エンジンにたどり着き、Rick Strahl による素晴らしいブログ投稿と、 angrypets.comの別のブログ投稿、およびSO に関するこの質問が見つかりました。これらのリンクは、私ができるよりもはるかに悲惨な詳細を説明していますが、Rick の投稿からのこの引用は、それをかなりうまく捉えています。

ここで本当の混乱が生じるのは、エラーは ASP.NET によってトラップされますが、最終的には引き続き IIS によって処理され、500 ステータス コードを確認してストック IIS エラー ページが返されるためです。

また、この動作は統合モードの IIS7 に固有のようです。msdnから:

IIS 7.0 でクラシック モードで実行している場合、TrySkipIisCustomErrors プロパティの既定値は true です。統合モードで実行している場合、TrySkipIisCustomErrors プロパティの既定値は false です。

したがって、基本的には、を 500 または 503 にResponse.TrySkipIisCustomErrors = true;設定するコードの直後に追加するだけで、すべてが設計どおりに機能するようになりました。Response.StatusCode

于 2009-11-12T02:13:18.783 に答える
0

GoDaddy で ASP.NET MVC サイトをホストしていますが、カスタム エラー ページの処理にも問題がありました。試行錯誤の結果、GoDaddy は HTTP レベルでエラーをインターセプトすることがわかりました。

たとえば、404 の HTTP ステータス コードを返したページは、GoDaddy のカスタム エラー ページに引き継がれました。最終的に、カスタム エラー ページを 200 ステータスを返すように変更したところ、404 関連の問題はなくなりました。私の HTML は同じで、HTTP ステータスだけを変更する必要がありました。

確かに、503 ステータス応答で同じことを試みたことはありませんが、同じ軽減策が機能する可能性があります。503 ステータスを返すことから 200 ステータスを返すように変更すると、問題は解決しますか?

この回避策を実行する場合は、検索エンジンがエラー ページをインデックスに登録しないようにする必要があることに注意してください。エラー ページが 200 ステータスを返すと、(検索エンジンの観点から) 通常のページと区別がつかなくなります。そのため、エラー ページのインデックス作成を防ぐために、必ず META ROBOTS タグを追加してください。

<META NAME="ROBOTS" CONTENT="NOINDEX">

このアプローチの欠点は、あなたのページが Google から削除される可能性があることです。これは間違いなく良いことではありません。

更新:さらに、ユーザー エージェントがクローラーであるかどうかを検出することもできます。クローラーの場合は 503 を返し、クローラーでない場合は 200 を返します。検出方法については、このブログ投稿を参照してください。クローラー。はい、クローラーとユーザーに異なるコンテンツを返すことは SEO の禁止事項であることはわかっていますが、これまでいくつかのサイトでこれを行ってきましたが、これまでのところ悪影響はありません。

奇妙なクローラーがボット検出器をすり抜けた場合に備えて、両方のアプローチ (META ROBOTS とボット検出) を実行するのが最善の策かもしれません。

于 2009-11-10T23:48:02.880 に答える