ASP.NET Core MVC 3.1 入門 その４ 「Routing」

# はじめに

ASP.NET Core MVC 3.1 の Routing について、自分が学んだことを備忘録として記載します。
このフレームワークに殆ど触れたことが無い方に少しでも参考になれば幸いです。
誤り等あれば、ご指摘頂けますと大変喜びます。

### 前回の記事

[ASP.NET Core MVC 3.1 入門 その３ 「Controller」](https://qiita.com/yagi405/items/5591d1f581db7978d3e8)

### 今回の流れ

* 挙動の確認
* Routingの説明
* Routeの説明
* Routeの定義
* デフォルトのRouteを変更してみる

### 今回のゴール

*  Routingとは何かを知る
*  `https://localhost:XXXXX/Hello/Index`とすると、なぜ`HelloController`の`Index`アクションメソッドが呼び出されるのかを理解する
*  デフォルトのRouteを変更できるようになる

### 環境
<dl>
    <dt>IDE</dt>
    <dd>Visual Studio 2019</dd>

    <dt>言語</dt>
    <dd>C#</dd>
</dl>


### 前提

以下2つのコントローラークラスが存在するものとします。
いずれもテキストデータを返すだけの`Index`アクションメソッドが定義されています。
アクションメソッドについては、[前回の記事](https://qiita.com/yagi405/items/5591d1f581db7978d3e8)を参照してください。

```cs
    public class HelloController : Controller
    {
        public IActionResult Index()
        {
            return Content("Hello World!");
        }
    }
```

```cs
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return Content("Home");
        }
    }
```

`StartUp`クラスはテンプレートのままです。

```cs
    public class Startup
    {
        //省略

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //省略

            app.UseRouting();

            //省略

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
```

### 挙動を確認する

実行してみると、以下のように`HomeController`の`Index`アクションメソッドが呼び出されています。理由は後述します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/1d73f790-1089-13fc-57e3-09bc736d3281.png)

ここでアドレスバーから手入力でURLを変更します。
変更前は以下のようになっているかと思います。
`XXXXX`の部分はポート番号を示しています。
環境とタイミング次第で異なりますので、ご自身の画面に表示されているものから変更する必要はありません。

```
https://localhost:XXXXX/
```

以下のようにアドレスバーから手入力でURLを変更してください。
末尾に`Hello/Index`を追加します。

```
https://localhost:XXXXX/Hello/Index
```

変更後、Enterを押下すると、以下のように「Hello World!」が表示されると思います。
これはつまり、`HelloController`の`Index`アクションメソッドが呼び出されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/2f942399-e80b-a3c3-208e-5fd6c9e05599.png)

ここで生じた2つの疑問について、仕組みを説明していきます。

* 実行すると、**なぜ`HomeController`の`Index`アクションメソッドが呼び出されるのか**
* `Hello/Index`とすると、**なぜ`HelloController`の`Index`アクションメソッドが呼び出されるのか**

### Routing

クライアントから要求されたURLに応じて、呼び出すコントローラーやアクションを決定する仕組みのことです。
ASP.NET Core MVC では、クライアントからの要求を受け取ると、まずは、Routingを利用して、呼び出すべきコントローラー（アクションメソッド）を決定します。

### Routingの流れ

1. リクエストのURLをアプリケーションが定義しているルート（Route）と照合します。
1. 一致するルートが見つかった場合は、該当するコントローラーのアクションメソッドが呼び出されます。  
※見つからなければ、404（NotFound）エラーとなります。

```
https://localhost:XXXXX/Hello/Index
```

従って`HelloController`の`Index`アクションメソッドが呼び出されるのは、上記の**URLをルートと照合した結果、一致するルートが見つかったから**ということになります。

### ルート（Route）

では、ルートとはどこでどのように定義されているのでしょうか。

ルートとは、URLからどのコントローラーに処理を関連づけるかのパターンマッチング文字列です。
プロジェクト直下の`Startup.cs`（`Configure`メソッド）で定義されています。

`Configure`はアプリケーションを起動する際に呼び出されるメソッドで、アプリの基本設定を宣言する場所ですね。

```cs
    public class Startup
    {
        //省略

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //省略

            app.UseRouting();

            //省略

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Hello}/{action=Index}/{id?}");
            });
        }
    }
```

`UseRouting`メソッドによって、Routingを有効化しています。
有効化されていない状態で実際の振り分け先を決める`UseEndpoints`メソッドを呼び出すとエラーになります。

ルートを定義しているのは、`MapControllerRoute`メソッドです。

```cs
	app.UseEndpoints(endpoints =>
	{
	    endpoints.MapControllerRoute(
	        name: "default",
	        pattern: "{controller=Home}/{action=Index}/{id?}");
	});
```

### MapControllerRouteメソッド

```cs
	endpoints.MapControllerRoute(
	    name: "default",
	    pattern: "{controller=Home}/{action=Index}/{id?}");
```

* **name**
`default`の部分
設定を識別するための名前です。
一意でさえあれば自由に設定可能です。
この時点では、複数設定できるということだけ抑えておけば問題ありません。

* **pattern**
`{controller=Home}/{action=Index}/{id?}`の部分
ルーティングの際にマッチングをおこなうためのURLパターンを示す文字列です。

### pattern

`{controller=Home}/{action=Index}/{id?}`とありますが

まずはシンプルに
`{controller}/{action}/{id}`と解釈してください。

`{XXX}`はプレースホルダーです。
`controller`と`action`は予約されている名前で、それぞれコントローラー名と
アクションメソッド名と紐づきます。
`{id}`にはアクションメソッドに渡される任意のパラメーターを設定することができます。

従って`~/Home/Index/5`であれば`HomeController`の`Index`アクションメソッドを呼び出し、パラメーターとして`5`を渡す という意味になります。


### patternの省略（デフォルト値）

では、説明を飛ばした`=Home`や`id`に付与されている`?`の意味も確認しておきましょう。

**デフォルト値**

省略された場合のデフォルト値を用意しておきたいならば、`{controller=Home}`のように書きます。（「`=`」以降がデフォルト値です）
上記の通り設定していた場合`http://localhost:XXXXX`とすると、コントローラー名とアクション名が省略されているため、デフォルト値が効いて、`HomeController`の`Index`アクションメソッドが呼び出されることになります。

* コントローラー名とアクション名を省略してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/1d73f790-1089-13fc-57e3-09bc736d3281.png)

**省略可能なパラメーター**

`{id?}`の「`?`」は、そのパラメーターが省略できることを示しています。
`{controller}/{action}/{id?}`であれば、`Home/Index/5`のようなパスだけでなく、`Home/Index`でもマッチします。

* `id`を省略してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/b6c2c3a0-6874-bf5e-b2ee-49269cd9fb9a.png)


### デフォルトのルートを変更してみる

`MapControllerRoute`メソッドを以下のように修正します。

```cs
	app.UseEndpoints(endpoints =>
	{
	    endpoints.MapControllerRoute(
	        name: "default",
	        pattern: "{controller=Hello}/{action=Index}/{id?}"); //Home => Hello
	});
```

実行すると、コントローラー名とアクション名が省略されているため、デフォルト値が効いて、`HelloController`の`Index`アクションメソッドが呼び出されています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/26693d2e-b8dc-985b-3526-ad9db17dfb08.png)

以上となります。
ありがとうございました。