你的浏览器还没开启 Javascript 功能!

ASP.NET Core 入門3 ASP.NET Core MVC ルーティング

前回に引き続きASP.NET Coreを学習メモです。今回はASP.NET Core MVC のルートについて学んでいきます。

前提

1. このコンテンツを扱うこと

  • ルートの原理
  • 様々なルーティングの設定
  • 規約ルーティングの設定
  • 属性ルーティング

2. 環境情報

環境/ソフトウェア内容
オペレーティングシステムWindows 10 1903
.NET Core SDK2.1.801
IDEVisual Studio Code 1.37.1
BrowserGoogle Chrome 76.0.3809.132

3. 前提知識

ルーティングの概要

1. ルーティングサービスとは

ASP.NET Core MVC はルーティングミドルウェアを使って、受信したHTTPリクエストのマッチングを行い、対応したコントローラーにがリクエストを処理できるようにします。

複数のルーティング設定はできますが、マッチングの順番は先に定義したルーティングが優先されます。

2. ルート設定のパラメータ

パラメータ説明
nameルーティング名称、任意だが重複してはいけない
templateルーティングテンプレート、URLのパターンや引数を定義する
dafaultルーティングテンプレートの引数の初期値を設定する
constraintsルート規約

MVCフレームワークは既存では2つのパラメータ(controller, action)があり、ルーティングマッチングした場合に、リクエストが処理される。つまり、この2つのパラメータが1つでも存在しないと、ルートが正しく動作できません。

ルーティング ミドルウェアの設定

1. 準備作業

前回作成したプログラムに対して、少し追加をします。Controllersフォルダに新しくTutorialController.csクラスを作成して、下記のように実装します。

using System;
using Microsoft.AspNetCore.Mvc;

namespace ds.Tutorial.web.Controllers {
    public class TutorialController : Controller {

        public IActionResult Index () {
            return Content ("ASP.NET Core Tutorial");
        }

        public IActionResult Welcome (string name, int age) {
            return Content ($"Welcome {name} (age: {age})!");
        }
    }
}

2. パラメータを使用したルーティング配置

Startup.csにルーティング設定を追加します。

routes.MapRoute (
    name: "TutorialPathValueRoute",
    template: "{controller}/{action}/{name}/{age}"
);

このルーティング設定にマッチングするURL

  • tutorial/welcome/dongsu/18

このルーティング設定にマッチングしないURL

  • tutorial/welcome/dongsu

もし、ageパラメータ無くてもURLをマッチングさせたい場合、ageを任意パラメータに変更することもできます。
{age}から{age?}に変更します。

routes.MapRoute (
    name: "TutorialPathValueRoute",
    template: "{controller}/{action}/{name}/{age?}"
);

このルーティング設定にマッチングするURL

  • tutorial/welcome/dongsu/18
  • tutorial/welcome/dongsu
  • tutorial/welcome/dongsu?age=18

3. カスタムルート

routes.MapRoute (
    name: "TutorialPrefixRoute",
    template: "dongchuan/{action}",
    defaults : new { controller = "Tutorial" }
);

このルーティング設定にマッチングするURL

  • dongchuan/index
  • dongchuan/welcome

templateにcontrollerの引数がないため、defaultsにcontrollerの初期値の指定が必要です。

規則ルーティング

1. ルート制約とは

ルートはルートパラメーターに関連付けされた検証ルールを用いて制約をかけることもできます。

制約かけるためにはStartup.csの名前空間に必要モジュールを追加します。

using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Constraints;

2. 文字列長さ制約

nameの長さが6桁越えること禁止

routes.MapRoute (
    name: "TutorialLengthRoute",
    template: "hello/{name}/{age?}",
    defaults : new { controller = "Tutorial", action = "Welcome", name = "dongsu" },
    constraints : new { name = new MaxLengthRouteConstraint (6) }
);

このルーティング設定にマッチングするURL

  • hello
  • hello/dongsu
  • hello/dongsu/18

このルーティング設定にマッチングしないURL

  • hello/dongsuaaa

3. 数値範囲制約

年齢(age)が1~100以内

routes.MapRoute (
    name: "TutorialMinMaxRoute",
    template: "yeah/{name}/{age?}",
    defaults : new { controller = "Tutorial", action = "Welcome", name = "dongsu" },
    constraints : new {
        age = new CompositeRouteConstraint (new IRouteConstraint[] {
            new IntRouteConstraint (),
                new MinRouteConstraint (1),
                new MaxRouteConstraint (100)
        })
    }
);

このルーティング設定にマッチングするURL

  • yeah/dongsu/1
  • yeah/dongsu/100

このルーティング設定にマッチングしないURL

  • yeah/dongsu/101
  • yeah/dongsu/1000

4. 正規表現を使った制約

routes.MapRoute (
    name: "TutorialRegexRoute",
    template: "welcome/{name}",
    defaults : new { controller = "Tutorial", action = "Welcome" },
    constraints : new { name = @"d[a-z]*" }
);

このルーティング設定にマッチングするURL

  • welcome/dongsu
  • welcome/dom
  • welcome/daisuke

このルーティング設定にマッチングしないURL

  • welcome/d1
  • welcome/dM
  • welcome/miwa

ここでnameに対して、正規表現を使って制約をかけました。

  • d[a-z*] アルファベットdで始まり、後に続く文字もアルファベット

5. カスタム制約

Commonフォルダを作成して、NameRouteConstraint.csのクラスを作成し、IRouteConstraintを実装します。

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

namespace ds.Tutorial.web.Common {
    public class NameRouteConstraint : IRouteConstraint {
        public bool Match (HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) {
            string name = values["name"]?.ToString ();
            if (name == null) {
                return true;
            }
            if (name.Contains ("admin")) {
                return false;
            }

            return true;
        }
    }
}

nameパラメーターにadminというキーワードを含んではいけないようにしました。

Startup.csに名前空間を追加

using ds.Tutorial.web.Common;

ConfigureServicesにカスタム制約を注入。

public void ConfigureServices (IServiceCollection services) {
    // MVCモジュールの導入
    services.AddMvc ();

    // 定義したカスタムルート制約
    services.Configure<RouteOptions> (options => {
        options.ConstraintMap.Add ("name", typeof (NameRouteConstraint));
    });
}

ルーティング設定

routes.MapRoute (
    name: "TutorialOkConstraintRoute",
    template: "ok/{name}",
    defaults : new { controller = "Tutorial", action = "Welcome" },
    constraints : new { name = new NameRouteConstraint () }
);

このルーティング設定にマッチングするURL

  • ok/dongsu
  • ok/sibata
  • ok/tom

このルーティング設定にマッチングしないURL

  • ok/admin
  • ok/rootadmin
  • ok/dongsuISadminROOT

属性ルーティング

属性ルーティングはコントローラーメソッドをURLにバインドする1つの方法です。
ルーティング設定ではなく、コントローラーメソッドに属性ルート(RouteAttribute)を使ってルートテンプレートを直接マップします。
内部ではそれらのルート属性により、システムのルートテーブルが生成されます。

ただし、コントローラーはルーティング設定or属性ルーティング1つしか使用できません。もしRouteAttributeでルーティング配置したら、Startup.csで配置したルーティング設定は無効になります。

Controllersフォルダで新しくTestController.csクラスを作成し、下記通りに実装します。

using System;
using Microsoft.AspNetCore.Mvc;

namespace ds.Tutorial.web.Controllers {
    [Route ("/test")]
    public class TestController : Controller {

        [Route ("")]
        [Route ("/test/home")]
        public IActionResult Index () =>
            Content ("ASP.NET Core RouteAttribute test");

        [Route ("servertime")]
        [Route ("/t/t")]
        public IActionResult Time () =>
            Content ($"ServerTime: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");

    }
}
ルート属性説明
[Route (“/test”)]このコントローラーリクエストURLが必ず/testで始まる
[Route (“”)]このアクションはリクエストURL/testに対して有効
[Route (“/test/home”)]このアクションはリクエストURL/test/homeに対して有効
[Route (“servertime”)]このアクションはリクエストURL/test/servertimeに対して有効
[Route (“/t/t”)]このアクションはリクエストURL/t/tに対して有効、Controllerのルート属性は無視できる

例えば[Route(“/test/home”)]なら、内部では以下のようなルーティング設定が生成されます。

routes.MapRoute(
    name: "Default",
    template: "test/home",
    defaults: new { controller = "Test", action = "Index" }
);

ルーティングはStartup.csで定義しても、Controllerで定義しても構いませんが、
個人的には特殊な場合を除き、下記のようにStartup.csにて初期値のルート定義だけで統一したほうが良いと思います。

開発者の決定すべきことをなるべく減少させ、ビジネスロジックに集中させたほうが効率的に開発できます。

routes.MapRoute (
    name: "Default",
    template: "{controller}/{action}",
    defaults : new { controller = "Home", action = "Index" }
);

備考

今回作成したソースコードです。

GitHubリポジトリ

では!!( `ー´)ノ