ASP.NET Core Razor Pagesで状態コードページを構成する


http の 404エラーなどに対応したページを構成したいという場合の対応です。

Startup.Configureメソッドで、

app.UseStatusCodePages();

を追加することで、 404などのステータスコードのページを提供することができるようです。

通常は、

app.UseStaticFiles();

の前に、書けば良いようです。

実際に確かめてみます。
確かに、以下のようなページが表示されました。

でも、あまりにもそっけないです。

UseStatusCodePagesWithRedirects メソッド

調べたら、UseStatusCodePagesWithRedirectsというメソッドがあるようです。

app.UseStatusCodePagesWithRedirects("/error/{0}");

とすれば、指定したページにリダイレクトされるようです。
すでに、Error.cshtml Error.cshtml.csがデフォルトで作成されているので、これで実行してみます。

あれ、だめです。「リダイレクトが繰り返し行われました。」とエラーになってしまいます。
OnGetメソッドが呼び出されません。

ブラウザのURLは、

https://localhost:5001/error/404

となるので、想定したURLにリダイレクトはされているようです。

でもソースコードをよくみたら、OnGetAsyncメソッドには、StatusCodeを受け取る引数がありません。

ああ、Error.cshtmlは、アプリケーション内での例外に対応するエラーページなのですね。

ということで、以下のように書き換えます。

app.UseStatusCodePagesWithRedirects("/statuserror/{0}");

statuserrorページも作成します。
Pagesフォルダーの下に、

StatusError.cshtmlとStatusError.cshtml.cs ファイルを以下のように作成します。

StatusError.cshtml.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace RazorPagesMovie.Pages
{
    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public class StatusErrorModel : PageModel
    {
        public int HttpStatusCode { get; set; }

        public IActionResult OnGet(int code)
        {
            HttpStatusCode = code;
            return Page();
        }
    }
}

StatusError.cshtml
@page "{code}"
@model RazorPagesMovie.Pages.StatusErrorModel
@{
    ViewData["Title"] = "StatusError";
}
<h3>エラーが発生しました。</h3>

<div>StatusCode : @Model.HttpStatusCode</div>

実行して、存在しないURLを指定してみます。

このcshtmlは、固定でメッセージを表示していますが、実際は、ステータスコードに対応するメッセージを表示するなどの対応が必要になると思います。

ASP.NET MVCの時は、Http Statusコードに対応したページを表示するには、Web.configで面倒な記述が必要だったんですが、そういったことなく実現できるのはありがたいです。

UseStatusCodePagesWithReExecute メソッド

UseStatusCodePagesWithReExecuteというメソッドもあるようです。

  app.UseStatusCodePagesWithReExecute("/statuserror","?code={0}");

こちらは、リダイレクトせずに同じURLのまま、エラーページが表示されます。

こちらに、変更してみます。

エラーの発生したURLを表示する

ページに表示された内容だけだと、どのURLでエラーが発生したのかがわかりません。まあ、ユーザにとってはあまり気にするところではありませんが、開発者側からするとこれが分かったほうが何かと便利です。

ということで、ページモデルを変更します。

using Microsoft.AspNetCore.Diagnostics;


   public class StatusErrorModel : PageModel
    {
        public int HttpStatusCode { get; set; }

        public string ErrorPath { get; set; }

        public IActionResult OnGet(int code)
        {
            HttpStatusCode = code;
            var feature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
            if (feature != null)
            {
                ErrorPath = feature.OriginalPathBase
                    + feature.OriginalPath + feature.OriginalQueryString;
            }
            return Page();
        }
    }

これで、どのURLを要求したのかがわかります。
Viewも ErrorPath を表示するように書き換えます。

@page
@model RazorPagesMovie.Pages.StatusErrorModel
@{
    ViewData["Title"] = "StatusError";
}
<h3>エラーが発生しました。</h3>

<div>StatusCode : @Model.HttpStatusCode</div>
<div>URL : @Model.ErrorPath</div>

実行してみます。
ブラウザのアドレスバーで、存在しないURLを入れてみます。

どこでエラーが発生したのかがわかるようになりました。