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


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

Startup.Configureメソッドで、


```cs
app.UseStatusCodePages();
```

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

通常は、

```c#
app.UseStaticFiles();
```

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


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

![スクリーンショット 2020-01-05 18.04.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29415/9d2e7138-c2e2-7de4-0c89-27085f64adf5.png "スクリーンショット 2020-01-05 18.04.31.png")


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

### UseStatusCodePagesWithRedirects メソッド

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

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

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

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

ブラウザのURLは、
```
https://localhost:5001/error/404
```

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

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

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



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

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

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

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


```c#: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();
        }
    }
}

```


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

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

```

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

![スクリーンショット 2020-01-05 18.09.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29415/89d30b36-64d7-3a12-f160-025b1b8aede6.png "スクリーンショット 2020-01-05 18.09.41.png")

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

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

### UseStatusCodePagesWithReExecute メソッド

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


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

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



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

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

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

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

```c#
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 を表示するように書き換えます。


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

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

```


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

![スクリーンショット 2020-01-05 18.47.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29415/5eb7c103-0ae3-0860-6650-1b4c463e2f3a.png "スクリーンショット 2020-01-05 18.47.48.png")

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