ASP.NET Core ルートパラメータに制約(入力チェック)をつける

2022年9月14日水曜日

ASP.NET

t f B! P L

enter image description here

はじめに

ASP.NET (Core)をはじめとする Webアプリでは、URLのパスにパラメータを含めるケースがよくある。

例えば、商品の詳細を表示するページにて、URLに https://exsampl.com/product/123456 のように商品を特定するキーを含めたり、カレンダーを表示するようなページでは https://exsampl.com/calendar/2022-09 のように日付をURLに含めることもあるだろう。

そして、ASP.NET (Core) では、こうした URLのパスに含めるパラメータのことをルート・パラメータと呼び、コントローラーのアクション(メソッド)で引数として指定されたパラメータを受け取れる。

■ ルートの定義(Startup.cs)

endpoints.MapControllerRoute(
          name: "product",
          pattern: "product/{productId}", new
      {
        controller = "Product",
        Action = "Index"
      });

■ コントローラ

public class ProductController : Controller

    [HttpGet]
    public IActionResult Index(string productId)
    {
        System.Console.WriteLine("productId=" + productId);
    }
}

ルート・パラメータに制約(入力チェック)をつける

さて、ここからが本題である。

ASP.NET (Core) のルート・パラメータは、URLのパスに含まれるパラメーターを、アクション(メソッド)の引数として容易に受け取れるが、ユーザーが自由に変更可能な URLをパラメータに使用する場合は、事前にパラメータの検証を行う必要がある。

1、2箇所程度しかルート・パラメータを使用しなのであれば、コントローラーのアクション(メソッド)内で入力検証を行ってもよいだろう。しかし、使用箇所が増えてくるとコードが煩雑になる。

そこで、IRouteConstraint の出番である。

IRouteConstraint は、ルート・パラメータに制約を定義するインタフェースで、条件に一致しないパラメータは、MapControllerRoute で指定したルーティングのルールにマッチしないため、不正なパラメータがコントローラーのアクション(メソッド)に流れるのを事前に防ぐことができる。

日付チェックする制約を作ってみよう

IRouteConstraint インタフェースの役割が分かったところで、実際にルート・パラメータが日付であるかチェックする制約を作ってみよう。

DateRouteConstraint という名前で新たにファイルを作り、IRouteConstraint インタフェースを実装する。

using System;
using System.Globalization;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Http;

public class DateRouteConstraint : IRouteConstraint
{

  public bool Match(
    HttpContext httpContext,
    IRouter route,
    string routeKey,
    RouteValueDictionary values,
    RouteDirection routeDirection)
  {
    //ここでルート・パラメータの検証を行う
  }

}

次に、ルート・パラメータの値を検証する Match メソッドの中身を実装する。

今回は、ルート・パラメータの値が yyyy-MM-dd 形式の文字列であれば true、それ以外の文字であれば false を返す処理にする。(言うまでもないが false を返すと制約条件NGとなる)

public bool Match(
  HttpContext httpContext,
  IRouter route,
  string routeKey,
  RouteValueDictionary values,
  RouteDirection routeDirection)
{
  if (values.TryGetValue(routeKey, out var routeValue))
  {
    var parameterValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);

    //パラメータが yyyy-MM-dd 形式の日付の場合 true(制約条件とマッチ)
    return DateTime.TryParseExact(parameterValueString, 
        "yyyy-MM-dd" , 
        null, 
        System.Globalization.DateTimeStyles.None, 
        out _);
  }
  return false;
}

制約を「ConstraintMap」に追加する

ASP.NET MVC (Core)であれば、Startup.csConfigureServices メソッドで、先ほど作った DateRouteConstraint クラスを制約リストに追加する。今回は date という名前で制約を追加した。

    public void ConfigureServices(IServiceCollection services)
    {
      services.Configure<RouteOptions>(routeOptions =>   
        {  
            routeOptions.ConstraintMap.Add("date", typeof(DateRouteConstraint));  
        });
    }

ルーティングで制約を設定する

次に、ルーティングで制約を設定する。

今回は、https://exsample.com/calendar/2022-12-31 のような URLを想定し、この URLの日付部分のルーティング・パラメータに、上で作成した制約を設定する。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

  app.UseEndpoints(endpoints =>
  {
    endpoints.MapControllerRoute(
              name: "calendar",
              pattern: "calendar/{dataValue:date}", new
          {
            controller = "Calendar",
            Action = "Index"
          });
  });
}

ポイントは {dataValue:date} となっている部分である。このようにルート・パラメータ名の後ろに :制約名 を付けることにより、URLのパターンマッチの際に制約のチェックが行われ、チェックNG(結果が false)の場合、コントローラーのアクションは実行されずに 404 などのエラーページが表示される。

まとめ

ASP.NET (Core)で、ルーティング・パラメータに制約を付ける方法を紹介しました。

煩雑になりがちなパラメータのチェック処理を、コントローラー側のコードから排除できる IRouteConstraint による制約を積極的に採用しましょう。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ