ASP.NET Core MVC 3.1 入門 その３ 「Controller」

# はじめに

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

### 前回の記事

[ASP.NET Core MVC 3.1 入門 その２ 「MVCプロジェクトの基本構造」](https://qiita.com/yagi405/items/4ab1c88d9ea3339b98ac)

### 今回の流れ

* MVCパターンの説明
* コントローラーの説明
* コントローラーを作ってみる
* アクションメソッドの説明
* 動かしてみる
* 振り返り

### 今回のゴール

*  MVCパターンにおけるコントローラーの役割を理解する
*  コントローラーを追加できる
*  アクションメソッドが何かを理解する

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

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

# MVCパターン

コントローラーの説明に入る前に、
まずはMVCパターンについて確認しておきましょう。
全体像を把握したうえで、コントローラーについて具体的にみていきます。

### MVCパターンとは

画面表示と、ビジネスロジックの分離を実現するためのアーキテクチャ（設計手法）です。

要はHTMLの生成といった画面表示のための処理と
DBアクセスのようなデータの管理/操作をするための処理
これらを分離させるために先人が考え出したプログラムの作り方の一つです。

MVCというのは構成要素の略称で、以下の単語の先頭文字をそれぞれ取り出したものです。

> Model – View – Controller

実現できると、それぞれが分離しているので変更・修正があった場合、お互いに影響を受けづらく、Testability (テスト容易性) が高まるというメリットがあります。

画面に表示する項目の書式変更によってDBアクセス処理に影響が出たり、
DBの軽微な定義変更によって画面表示処理に影響が出るといった事態が発生しづらくなります。

### 各要素の役割

| 要素       | 概要                                                                                       |
|------------|--------------------------------------------------------------------------------------------|
| Model      | データの管理/操作といったビジネスロジックを担当                                      |
| View       | データ入力用のフォーム、処理結果の表示といったアプリケーションのフロントエンドを担当 |
| Controller | ModelとViewの橋渡しを担当                                                            |

# コントローラー

### コントローラーとは

MVCの全体像を把握したうえで、 **コントローラー**（Controller）について具体的にみていきます。
MVCにおいて、 **Model** と **View** の橋渡しを担当する要素です。

>  **View** から受け取った生データを **Model**に引き渡し、
 **Model** での処理結果を **View** に返す

というのが主なお仕事です。

よくある会員登録処理で、 **コントローラー** のお仕事を確認してみましょう。

1. 会員登録フォームに入力された情報を **View** から受け取る  
→ **View** から生データを受け取る
1. 実際の会員登録処理(DBアクセス)を担当する **Model** に情報を引き渡す  
→ 受け取ったデータを **Model** に引き渡す
1. 会員登録の処理結果(成功/既登録により失敗 etc)を受け取る  
→ **Model** から処理結果を受け取る
1. ユーザーに会員登録の処理結果を通知するために、 **View** へ処理結果を返してあげる  
→ 受け取った処理結果を **View** に返す

#  コントローラーを追加する

では実際にコントローラーを作ってみましょう。

### ASP.NET Core MVC 3.1 のプロジェクトを作成する

まずは「Web アプリケーション（モデル ビュー コントローラー）」テンプレートでプロジェクトを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/dbc8dc05-2d6d-1f8e-79a8-719fad551838.png)

### ソリューションエクスプローラーを眺める

各フォルダ/ファイルの概要については[前回の記事](https://qiita.com/yagi405/items/4ab1c88d9ea3339b98ac)をご参照ください。
お作法として、 **コントローラー** は必ず`Controllers`フォルダ配下に配置してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/77bce464-1cc1-fb12-10c4-9de902e6a9d3.png)

###  **コントローラー** の追加

既にテンプレートに含まれている`HomeController`が存在しますが、
新しいコントローラーを追加してみます。
> Controllersフォルダを右クリック > 追加 > コントローラー 

と進んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/5499e70c-f113-60f8-fbcc-afab08431526.png)

### スキャフォールディング

以下のようなウィンドウが立ち上がると思います。
「スキャフォールディング」というのは、要は**雛形を自動生成する機能**のことを示します。
今回はコントローラーの雛形を自動生成してもらうために、この機能を使います。

最初から全ての コントローラーの基底クラスとなる`Controller`クラスを継承してくれたり、読み取りや書き込み用のメソッドを用意してくれたりと、手間が省けるので、 コントローラーを追加する際には普通にクラスとして追加するのではなく、この手順でスキャフォールディング機能を利用することをおすすめします。

今回は「MVC コントローラー - 空」を選択します。
よりリッチな雛形（CRUDに相当するメソッドが用意されている）もありますが、まずはまっさらなコントローラーを使っていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/cdf76139-0532-5ba6-a790-cdcfd4f6f2b0.png)

続いてファイル名ですが、`HelloController`とします。
ここで重要なのは**末尾を`Controller`とする**ことです。
末尾が`Controller`でないと、そのままではコントローラーとして検出されません。
※末尾が`Controller`でない場合は、`Controller`属性を付与する必要があります。
クラス名から`Controller`を取り除いた部分がコントローラー名とみなされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/fe4fa7ba-5ad3-ca5e-d134-d55185349cb0.png)


#  コントローラークラス

以上でコントローラーは追加できたと思います。
では、追加されたコントローラーのコードを見ていきましょう。

### HelloController

まず注目して頂きたいのは、**`Controller`クラスを継承している**点です。
コントローラーは通常`Microsoft.AspNetCore.Mvc.Controller`クラスを継承します。
※前身のASP.NET MVC では必須でした。

```cs

    public class HelloController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
```

# アクションメソッド

次に`Index`というメソッドをみていきます。
今回は「MVC コントローラー - 空」を使用しましたが、この`Index`メソッドは最初から定義されています。

このメソッドは「**アクションメソッド**」と呼ばれるものです。

```cs
    public IActionResult Index()
    {
        return View();
    }
```

### アクションメソッドとは

**クライアントからの1リクエスト(ex 登録ボタンの押下)に対して、具体的な処理内容を定義するメソッド**です。
上記の`Index`メソッドが正にアクションメソッドとなります。
Controllerクラスとは、関連性を持った複数のアクションメソッドによって構成されます。
逆に言えば、一つ以上のアクションメソッドを束ねたクラスがコントローラーとなります。

### アクションメソッドの規約

メソッドをアクションメソッドとして定義するためには、以下に代表される規約を全て守る必要があります。

* コントローラークラス内に定義する
* `public`
* `static` でない
* 戻り値が`IActionResult`オブジェクト（後述）

上記の`Index`アクションメソッドは上記の規約を全て守っているので、アクションメソッドということになります。

### アクションメソッドの戻り値

繰り返しになりますが、アクションメソッドとは**クライアントからの1リクエスト(ex 登録ボタンの押下)に対して、具体的な処理内容を定義するメソッド**です。

従って、様々な処理をおこなうことが予想されます。
当然、様々な処理の結果、結果も様々となります。

* 文字列
* ファイルのデータ
* 別のページへの遷移（リダイレクト）
* 要求されたページが存在しない という結果

上記のような様々な結果を表現するために 「[IActionResult](https://docs.microsoft.com/ja-jp/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1#iactionresult-type)」を実装した様々なクラス（`IActionResult`オブジェクト）が用意されています。

この`IActionResult`インターフェースはアクションメソッド内での処理に代わり、実際のHTTPレスポンスを生成する機能を提供しています。
我々が、HTTPレスポンスを直接的に意識することなく、開発ができるのはこの仕組みのおかげというわけです。

代表的な`IActionResult`オブジェクトを以下で紹介します。

### 主なIActionResultオブジェクト

| 型                     | 概要                                          |
|------------------------|-----------------------------------------------|
| ContentResult          | テキストデータを返す                          |
| JsonResult             | JSON 形式のデータを返す                       |
| ViewResult             | テンプレート（ビュー）に基づいてページを返す  |
| FileContentResult      | 指定されたバイト配列をファイルとして返す      |
| RedirectResult         | 指定のアドレスに移動（リダイレクト）する      |
| RedirectToActionResult | 指定のアクションに移動（リダイレクト）する    |
| NotFoundResult         | ステータスコード 404（NotFound）エラー を返す |
| EmptyResult            | なにもしない                                  |

利用頻度の高い`IActionResult`オブジェクトについては、対応するファクトリメソッドが`Controller`クラスに用意されています。
対応するファクトリメソッドが存在しないものについては、自分で`new`する必要があります。

代表的なものを以下に紹介します。

### 主なIActionResultオブジェクトのファクトリメソッド

| ファクトリメソッド | IActionResultオブジェクト | 備考                                                                                                                                           |
|--------------------|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| Content            | ContentResult             |                                                                                                                                                |
| Json               | JsonResult            |                                                                                                                                                |
| View               | ViewResult                | デフォルトでは「/Views/コントローラー名/アクション名.cshtml」を読み込む                                                                        |
| File               | FileContentResult         |                                                                                                                  |
| Redirect           | RedirectResult            | リダイレクト先をURLで指定する                                                                                                                  |
| RedirectToAction   | RedirectToActionResult    | リダイレクト先をアクション名（＋コントローラー名）で指定する                                                                                |
| NotFound           | NotFoundResult            | ステータスコードを返却する場合は、StatusCodeResultクラスを利用することもできる(対応するファクトリメソッドがないステータスコードも存在する) |

# 動作確認

### アクションメソッドを修正する

自動生成された`Index`アクションメソッドを以下のように修正します。
現時点では、Viewを追加していないので、Viewテンプレートを経由せずにテキストデータを返す`ContentResult`を戻り値として返してみます。
`Content`メソッドは、上述した通り`ContentResult`のファクトリメソッドです。

```cs

    public class HelloController : Controller
    {
        public IActionResult Index()
        {
            return Content("Hello World!"); //修正箇所
        }
    }
```

### 実行

修正したら、F5で実行してみましょう。
テンプレートで用意されているトップページが表示されると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/97907585-c1cf-645b-981c-34cfb9ee95b9.png)

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

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

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

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

変更後、Enterを押下すると、以下のように「Hello World!」表示されると思います。
以上で動作確認は終了です。

`Hello/Index`とすると、**なぜ`HelloController`の`Index`アクションメソッドが呼び出せるのか**については、次回以降の記事にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544508/d6c09cb6-14bc-342b-15f0-ef8416d39712.png)


# アクションメソッドを振り返る

ここまでのおさらいも兼ねて、以下のメソッドを言葉にすると

> `Content` メソッドを呼び出し、テキストデータを返すための `IActionResult` オブジェクト（`ContentResult`）を生成し、戻り値として返す `Index` アクションメソッド 

ということになります。


```cs

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