ASP.NETでMVCをつまみたい

昔、Spring Boot上でのウェブアプリケーション開発に従事していましたが、忙しさゆえにMVCをよく理解しないでコーディングしていました。。。
後悔の念が今でもあるので、改めて再入門しようと決意。
せっかくなので新しい知見も得たいと思い、C#の実務経験もあるので、ASP.NETで学習してみることとします。
皆様のマサカリお待ちしています。

# 0.学習前の私のプロフィール
* C#実務経験3年ぐらい(ASP.NETではない)
* MVCよく分かんない
* ASP.NETって何？　ASPなら知ってるよ

# 1.開発環境
OS: Windows10 Home
IDE: Microsoft Visual Studio Community 2019
ブラウザ: GooglrChrome ver87.0.4280.141

# 2.チュートリアル

こういうのは公式のチュートリアルからやってみるに限ります。
[チュートリアル](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/first-mvc-app/?view=aspnetcore-5.0)

ひとまずプロジェクトを作成します。「ASP.NET Core Web アプリケーション」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/9ed936a0-9d92-827f-8c2a-012543c222d3.png)

「プロジェクト名」と「ソリューション名」は適当です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/996fb004-e087-ba7b-d3c3-65d98cacf8ac.png)

「Webアプリケーション（モデルビューコントローラー）」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/0797378d-ed2e-394c-b68d-becd9dd9958b.png)

プロジェクトは出来たら、とりあえず、「Ctrl + F5」で実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/67d3b50d-0526-f30a-a888-80ae7ed6bc11.png)
すると証明書うんぬん聞かれるのでオレオレ証明書を信頼することとします。「はい」を押下。
次の警告も「はい」

おー、できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/8988eeae-0b53-35ee-1fed-16ef3e4eb1f0.png)

# 3.できてない
最近のフレームワークはとりあえずインストールするだけで、あれよあれよと形になってしまうのがいけませんね。
これで終わっては学習にならないので、ひとまず座学から。
MVCとは、
>MVC（Model View Controller モデル・ビュー・コントローラ）は、ユーザーインタフェースをもつアプリケーションソフトウェアを実装するためのデザインパターンである。
>アプリケーションソフトウェアの内部データを、ユーザーが直接参照・編集する情報から分離する。そのためにアプリケーションソフトウェアを以下の3つの部分に分割する。
>model: アプリケーションデータ、ビジネスルール、ロジック、関数
>view: グラフや図などの任意の情報表現
>controller: 入力を受け取りmodelとviewへの命令に変換する

https://ja.wikipedia.org/wiki/Model_View_Controller

また、MVCのパターンで設計するメリットは以下となります。
>MVC パターンは、これらの要素間の疎結合を提供しながら、アプリのさまざまな側面 (入力ロジック、ビジネス ロジック、および UI ロジック) を分離するアプリを作成するのに役立ちます。 
>(略)このように分離することで、他のコードに影響を与えることなく、実装の 1 つの側面に専念できるため、アプリを構築するときの複雑さが管理しやすくなります。

https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/first-mvc-app/adding-controller?view=aspnetcore-5.0&tabs=visual-studio

なるほど、発想としては目新しくなく、複雑なものを作り上げるためには設計の分離が必要である。ということですね。（おそらく理解としては浅い。。。）

ひとまず、実務的な部分で言えば、以下が言えるようです。

* Viewは主にUIを表し、Modelにはデータを格納し、ControllerがViewとModelの橋渡しを行う。
* ViewとModelでやり取りを行うのは原則禁止。
* MVCパターンで実装すると各層のロジックが疎となり、それぞれの階層でのテストが実装しやすい。

その他にも理解を深めるためにいろんな人のサイトも見回りましたが、MVC自体がデザインパターンであるためか抽象度が高く、様々な主張があるため、逆に混乱。。。

あまり厳密にとらえようとしても、沼にはまってしまうため、ひとまず手を動かして勉強しようと思います。よし、次だ次！

# 4.Controller
早速、Controllerクラスを実装してみます。(学習順はチュートリアルに準拠します)
と、その前に、まずクライアントが`localhost:{Port}`にアクセスしたときのルーティング設定について、
ルーティング設定は`Startup.cs`ファイル内の`Configure`メソッドで設定されます。(MVCプロジェクト作成時に自動で生成されているはずです)

```C#:Startup.cs
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
        // ~~略~~
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
```
上記のコードは特にURLに何も指定しなかった場合、`{controller=Home}`とあるように`HomeController`が呼び出されることを意味しています。

また、MVC によって使われるURLルーティングロジックでは、次のような形式を使って呼び出すコードが決定されます。

`/[Controller]/[ActionName]/[Parameters]`

今回で言えば、`"{controller=Home}/{action=Index}/{id?}"`の部分ですね。

## 4.1 HelloWorld(入門)

さて、では、自分でControllerを作成してみましょう。
ソリューションエクスプローラーでプロジェクト内の「Controller」を右クリックし、「追加」->「新しい項目」->「空のMVCコントローラー」を選択。
このときに設定する「名前」がそのままURLで指定する文字列と紐付きます。
例えば、コントローラー`HelloWorld`を作成する場合は、名前を`HelloWorldController`としましょう。なお「名前」の末尾は必ず「Controller」でなければなりません。

作成した`HelloWorldController.cs`にコードを追加します。

```C#:HelloWorldController.cs
    public class HelloWroldController : Controller
    {
        /// <summary>
        /// HelloWorld
        /// </summary>
        public string Index()
        {
            return "Hello World";
        }

        /// <summary>
        /// HelloWorld/Welcom
        /// </summary>
        public string Welcom()
        {
            return "Welcom to my page !";
        }

    }
```

上の方の`Index()`メソッドはユーザが`localhost:{PORT}/HelloWorld`自体にアクセスした際に返却されるGetメソッドです。
また同様に、`Welcom()`メソッドはユーザが`localhost:{PORT}/HelloWorld/Welcom`にアクセスした際に返却されるGetメソッドです。
このようにコントローラーに定義されたHTTPメソッドのことを総称してアクションメソッドと呼ぶみたいです。

実行結果
`localhost:{PORT}/HelloWorld`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/b8bdb52c-c75c-7962-c4da-2228cd376e88.png)

`localhost:{PORT}/HelloWorld/Welcom`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/209cbc1c-7155-85dd-911b-6b279914b57a.png)


今はチュートリアルなので、単純にハードコーディング的な文字列を返していますが、本来であればここでViewを返します。


## 4.2 クエリパラメータ

URLクエリパラメータを設定するには、アクションメソッドに引数を設定します。
`HelloWorldController.cs`の`Welcom()`を下記のように変更します。

```C#:HelloWorldController.cs

using System.Text.Encodings.Web;

        public string Welcom(bool kimari, string partyJob = "summoner")
        {
            if(kimari == true)
            {
                return "キマリは通さない。";
            }
            else 
            {
                return $"{partyJob}は通す。";
            }

        }
```

概ね挙動に予想は付くので、実行結果。

`https://localhost:{PORT}/helloworld/welcom?kimari=false`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/bf36cf4c-023d-c7a4-f2d1-ca8015b9fcc5.png)
`https://localhost:{PORT}/helloworld/welcom?kimari=false&partyjob=guard`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/2a3ca2ad-e52f-2aa5-5cfd-2bdee007b949.png)
`https://localhost:{PORT}/helloworld/welcom?kimari=true`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/9fdb79fa-a8a8-0830-59e8-123594d43a28.png)

# 5.View

MVCモデルではViewはユーザとアプリケーションのインターフェイスの役割を担います。
ASP.NETではRazorというエンジンを用いることで、動的なViewを作成することができます。

## 5.1 Razor
Razor(レイザー)とは、ASP.NET上で動作するエンジンビューもしくはマークアップ構文を指します。
かみ砕くと、WebページにC#ライクのコードを埋め込むテンプレートエンジンみたいなものでしょうか。

```C#
@{
    var hoge = new string[] {"R","a","z","o","r"};
}
```

このようにHTMLの中に`@{}`で囲んだブロック記述し、その中にC#ライクなコードが記述できます。

## 5.2 View

先ほど作成した`HelloWorldController.cs`の`index()`メソッドを下記のように書き換えます。

```C#
        public IActionResult Index()
        {
            return View();
        }
```

アクションメソッドはviewを返す場合、この`IActionResult`というインターフェイスを返却するのがお作法です。

次にソリューションエクスプローラー上で、`View`フォルダ内に新たなフォルダ`HelloWorld`を作成。この名前がControllerと紐づくのでフォルダ名はお間違えなく。
その後、`HelloWorld`フォルダを右クリックし、「追加」->「新しい項目」->「Razorビュー　- 空」を選択。
生成された`index.cshtml`内に以下のコードを記述します。

```HTML:index.cshtml
@{
    @* 文字列Hoge *@
    var hoge = new string[] { "R", "a", "z", "o", "r" };
}

<h2>Hello World</h2>

<ul>
    @{ foreach (var s in hoge)
        {
            <li>@s</li>
        }
     }
</ul>
```

実行結果 `https://localhost:{PORT}/helloworld`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/ecd3124e-c718-7f59-ed9d-fd87a1a6e3a1.png)

正しくRazorエンジンが動作し、動的なページを作成することが出来ました。

また、上記のページは、以下の流れで表示されています。
1. `https://localhost:{PORT}/helloworld`のリクエスト
2. `HelloWorldController.cs`の`index()`というアクションメソッドが実行される
3. `HelloWorld`配下にあるView`index.cshtml`がレスポンスされる

まず、Controllerがユーザリクエストを捌く。このときControllerはViewを返すこと以外に責務を負わず、実際のWebページの生成はView側にて行う。互いのロジックを疎にすることで役割分担が出来ました。
これでM **VC**のVCは実装できました。後はModelです。

# 6.Model

最後にModelです。
Modelはデータベース操作及びビジネスロジックを担当します。
いや、それ対応範囲広くない？ViewとController以外の全部じゃない？とも思いましたけど、リクエスト処理やUIにビジネスロジックが混ざらないだけで、設計として意味があるんだと思います。（多分）
今回は簡易的にデータモデルとしてエンティティクラスを作成して、Viewに渡すところまでを実装します。

`Models`フォルダを右クリックし、「追加」->「新しい項目」->「クラス」を選択。
以下のクラスをそれぞれ作成します。

```C#:HelloWorldModel.cs
    public class HelloWorldModel
    {
        public int id { get; set; }
        public string Name { get; set; }
        public long Price { get; set; }

    }
```

```C#:RamenFactory.cs
   public class RamenFactory { 
        static public List<HelloWorldModel> MakeRamens()
        {
            var ramens = new List<HelloWorldModel>() {
                     new HelloWorldModel{ id= 1, Name="塩ラーメン", Price= 1000},
                     new HelloWorldModel{ id= 2, Name="醤油ラーメン", Price= 900},
                     new HelloWorldModel{ id= 3, Name="味噌ラーメン", Price= 1100}
                };
            return ramens;
        }
    }
```

また、`HelloWorldController.cs`に以下のアクションメソッドを実装し、
`View/HelloWorld`配下に新たなView`ModelTest.cshtml`を作成します。

```C#:RamenFactory.cs
using Hello_World.Models;

        public IActionResult ModelTest()
        {
            var ramens = RamenFactory.MakeRamens();
            //第一引数に実行Viewのパスを指定することが可能。
            return View("~/Views/HelloWorld/ModelTest.cshtml", ramens);
        }
```

```html:ModelTest.cshtml
@model IEnumerable<Hello_World.Models.HelloWorldModel>

<h2>Model Test</h2>

@foreach (var m in Model)
{
    @Html.DisplayFor(modelItem => m.id)
    <br />
    @Html.DisplayFor(modelItem => m.Name)
    <br />
    @Html.DisplayFor(modelItem => m.Price)
    <br />
    <hr />
}
```

~~Viewにリストを渡してRazorで処理を行う部分の実装がなかなか上手くいかず苦労しました。~~
コントローラーから受け取ったModelは`@model`で宣言することにより、View内で`Model`として静的型付けを行えます。
また、Viewに渡したオブジェクトの内容を表示したい場合、`@Html.DisplayFor`でプロパティの内容を表示します。

実行結果 `https://localhost:{PORT}/helloworld/testmodel`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/08f0cfb6-117c-148f-6581-76d98578ba4c.png)

この通りModelの内容をViewで表示することができました。

非常に簡易的ですが、これにてMVCの最小単位をASP.NETで実装することが出来ました。

# 7.Next
本記事の学習でMVCの概念的なところは学べてた思います。
次回以降は今回学んだMVCというアーキテクトで実際にアプリケーションを作成したいと考えています。

# 参考
[MicroSoft_チュートリアル](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/first-mvc-app/?view=aspnetcore-5.0)
[ASP.NET 入門](https://aspnet.keicode.com/)
[【ASP.NET MVC,C#】一つのメソッドで複数のViewを切り替える方法](https://tekitoumemo.hatenablog.com/entry/2018/03/02/201920)
[ASP.NET MVC のビューにモデルのリスト形式のオブジェクトを渡す - ASP.NET MVCプログラミング](http://surferonwww.info/BlogEngine/post/2013/03/27/editorfor-and-displayfor-display-different-values.aspx)