しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET Core でもステータスコード単位でカスタムエラーページを表示したい

ASP.NET Core でも UseStatusCodePages を使えば 400-599 のステータスコードの場合に、専用のページを表示できることを前に書きました。最低限必要な機能はちゃんと揃っています。

しかし UseStatusCodePages を使って対応できるのは、ステータスコードを URL フォーマットとして埋め込んで、後はコントローラ側に任せるという形です。

IIS や ASP.NET ではあったステータスコード単位でページを指定する方法は、そのままでは使えません。必要かどうかはともかくとして、今回は必要なケースがあったので対応してみました。

アクションにステータスコードを埋め込む

割とすぐに思いつく方法は、アクション名をそのままステータスコードにしてしまうことでしょう。メソッド名として 404 などは書けないですが、ルーティングレベルでは書けます。

[Route("[controller]")]
public class ErrorController : Controller
{
    [Route("404")]
    public IActionResult NotFound()
    {
        return View();
    }
    
    [Route("500")]
    public IActionResult ServerError()
    {
        return View();
    }
}

上のようなコントローラを用意すれば、後は Startup で設定するだけです。

app.UseStatusCodePagesWithRedirects("~/Error/{0}");

これで実行すると、ちゃんと存在しないページにアクセスした場合に 404 ページが表示されます。

一応これで目的は果たせますが、指定外のステータスコードの扱いがちょっと面倒です。

Middleware レベルで振り分ける

ルーティングレベルで解決するのもアレだったので、もう一つ UseStatusCodePages に用意されているオーバーロードを使って解決してみることにしました。

オーバーロードには HttpContext に触れるものがあるので、そこでステータスコードを見てリダイレクト先を変更するという方法です。以下のようなコントローラを用意します。

[Route("[controller]/[action]")]
public class ErrorController : Controller
{
    public IActionResult NotFound()
    {
        Response.StatusCode = 404;

        return View();
    }
    
    public IActionResult ServerError()
    {
        Response.StatusCode = 500;

        return View();
    }
}

MVC 5 時代からありそうなコントローラです。ステータスコードを返すのに TrySkipIisCustomErrors = true を使う必要がなくなったのは地味に嬉しいと感じるポイントです。

そして Startup ではステータスコードを見てリダイレクト先を変えるコードを書きます。

app.UseStatusCodePages(context =>
{
    if (context.HttpContext.Response.StatusCode == 404)
    {
        context.HttpContext.Response.Redirect("/Error/NotFound");
    }
    else
    {
        context.HttpContext.Response.Redirect("/Error/ServerError");
    }

    return Task.CompletedTask;
});

やっていることは単純なので特に説明しませんが、UseStatusCodePages を使って目的を果たすことが出来ました。拡張メソッドを用意して、もうちょっとスマートに書けるようにも出来そうです。

f:id:shiba-yan:20180604172056p:plain

実行すると、ちゃんと指定したページへリダイレクトされます。

この辺りを調べているときに IApplicationBuilder が結構面白いことに気が付いたので、もうちょっと深堀してみようかと思います。