ASP.NET Coreアプリケーション最小構成チートシート

#TL;DR

本当にチートシート。以下の設定を行えばOK。

* csproj

```xml:App.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>netcoreapp2.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
        <!--本当の最小構成-->
        <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.3" />
        <!--ASP.NET Core MVC(Web API)を使う場合に追加-->
        <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.4" />
        <!--ASP.NET Core MVC(Web Site)を使う場合に追加-->
        <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.4" />
    </ItemGroup>

</Project>
```

* `Program.cs`

```csharp:Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = new WebHostBuilder()
            .UseKestrel() // Kestrelを使用する
            .UseContentRoot(Directory.GetCurrentDirectory()) // プロジェクトディレクトリを基点にする
            .UseStartup<Startup>() // Startupの構成を読み込む
            .Build();

        builder.Run();
    }
}
```

* `Srartup.cs`

```csharp:Startup.cs
public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IHostingEnvironment environment)
    {
        // 構成ファイルと環境変数を読み込んで保存
        Configuration = new ConfigurationBuilder()
            .SetBasePath(environment.ContentRootPath)
            .AddJsonFile("appsettings.json", false, true)
            .AddEnvironmentVariables()
            .Build();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        // ASP.NET Core MVC(Web API)周りのDI解決を有効化
        services.AddMvcCore();
        // ASP.NET Core MVC(Web Site)周りのDI解決を有効化
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // HTTPリクエストを受け取って、HTTPレスポンスを返すだけ
        app.Run(async context => await context.Response.WriteAsync("Hello World!"));

        // ASP.NET Core MVCのルーティングを使用
        app.UseMvc();
    }
}
```

* コントローラ
    * Web APIの場合は`ControllerBase`を継承するだけ。
        * 各HTTPステータスコードを返す`IActionResult`のメソッドは`ControllerBase`にある。
        * swaggerのドキュメント生成も`ControllerBase`を探して生成している。
    * Web Siteの場合は`Controller`を継承する必要がある。
        * ページを返す`IActionResult`は`Controller`にある。

# はじめに

Visual Studio 2017でASP.NET Coreアプリケーションのプロジェクトを作成すると、どのアプリケーション形式を選択しても`Microsoft.AspNetCore.All`をいうパッケージを参照したプロジェクトが作成されます。

この`Microsoft.AspNetCore.All`パッケージは結構な量のパッケージをまとめたメタパッケージで、確かにこれを参照すればASP.NET Coreを使用したWebアプリケーションを開発するために必要なものが入っているのですが、場合によっては不要なパッケージの参照も含まれているため一部の人にとっては嬉しくないパッケージになっています。

また、デフォルトで作成された構成設定のソースコードもブラックボックス化されており、本当に必要な構成だけが設定されているのかどうかわかりにくくなっています。

私も余計な参照設定は極力なくして開発をするため、この`Microsoft.AspNetCore.All`に依存せずに必要最低限なパッケージ参照と構成設定で開発を始めるためのメモとして残しておきます。

以降は最小構成の設定から徐々に機能を追加していくように記述してあります。

# Kestrel上で動くWebサーバーを構築する場合

KestrelというASP.NET Coreが内包しているWebサーバーを使用して、単純にHTTPリクエストを受け付けてHTTPレスポンスを返すASP.NET Coreアプリケーションを構築します。
恐らくこの構成がASP.NET Coreアプリケーションの一番小さい構成になると思います。

まずはcsprojファイルを開いて`Microsoft.AspNetCore.All`のパッケージを削除し、`Microsoft.AspNetCore.Server.Kestrel`パッケージを追加します。

```xml:App.csproj
<ItemGroup>
    <!--↓を削除して…-->
    <!--<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />-->
    <!--↓を追加する-->
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.3" />
</ItemGroup>
```
次に`Program.cs`を開いて、以下のようにコードを書き直します。

```csharp:Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = new WebHostBuilder()
            .UseKestrel() // Kestrelサーバーを有効化する
            .UseStartup<Startup>() // Startupクラスを使用する
            .Build();

        builder.Run();
    }
}
```

最後に`Startup.cs`を開いて、コードを以下のように書き直します。

```csharp:Startup.cs
public class Startup
{
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // リクエストを受け付けたら「Hello World!」と出力する
        app.Run(async context => await context.Response.WriteAsync("Hello World!"));
    }
}
```

ここまで書き直したら、アプリケーションを起動してみましょう。サーバーが立ち上がってWebブラウザで既定のURLにアクセスすると以下のように表示されると思います。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/39403/4073652b-58fd-2a1a-000f-eb3c4104bd77.png)

ちなみに`Program.cs`で以下のように記述すれば`Startup.cs`を用意しなくても済みます。

```csharp:Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = new WebHostBuilder()
            .UseKestrel()
            // UseStartupの代わりに以下を記述する
            .Configure(app => app.Run(async context => await context.Response.WriteAsync("Hello World!")))
            .Build();

        builder.Run();
    }
}
```

# Kestrel上で動くASP.NET Core MVC(Web API)を構築する

KestrelのみのWebサーバーだと複数のURLを扱うのに手間がかかるので、次はASP.NET Core MVCを使用したWeb APIサーバーを構築します。
これを使えばURLルーティング機能の付いたWeb APIサーバーの構築が出来ます。(実際にはもっと色んな機能が付いてきますが。)

csprojファイルを開いて`Microsoft.AspNetCore.Mvc.Core`パッケージを追加します。

```xml:App.csproj
<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.4" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.3" />
</ItemGroup>
```

次に`Startup.cs`を以下のように修正します。

```csharp:Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Dependency InjectionでASP.NET Core MVC周りの機能を解決できるようにする
        services.AddMvcCore();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // ASP.NET Core MVCのルーティングを有効化する
        app.UseMvc(route =>
        {
            // とりあえず[コントローラ名]/[アクション名]でURLをルーティング
            route.MapRoute("default", "{controller}/{action}");
        });
    }
}
```

最後にコントローラを追加します。今回は`ValueController.cs`をいうファイル名にしてプロジェクトディレクトリに作成したControllersフォルダに配置しましょう。(プロジェクトディレクトリ直下に配置しても構わないんですが後々のメンテナビリティのためにフォルダを切っておくことをオススメします。)

```csharp:ValueController.cs
// ControllerではなくControllerBaseを継承させる
public class ValueController : ControllerBase
{
    public string Index()
    {
        return "value";
    }
}
```

これでアプリケーションを実行して、`/Value/Index`にアクセスしてみると以下のように表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/39403/fb131f04-5728-7325-50d4-bc0027c447c5.png)

# Kestrelサーバー上で動くASP.NET Core MVC(Web Site)を構築する

今度は画面のあるWebアプリケーションを構築します。

csprojファイルで`Microsoft.AspNetCore.Mvc.Core`パッケージの参照を削除し、`Microsoft.AspNetCore.Mvc`パッケージの参照を追加します。

```xml:App.csproj
<ItemGroup>
    <!--↓を削除して…-->
    <!--<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.4" />-->
    <!--↓を追加する-->
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.4" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.3" />
</ItemGroup>
```

次に`Startup.cs`を以下のように修正します。

```csharp:Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Dependency InjectionでASP.NET Core MVCのView周りの機能を解決できるようにする
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMvc(route =>
        {
            route.MapRoute("default", "{controller}/{action}");
        });
    }
}
```

コントローラとビューを追加します。コントローラはさっき作成したControllersフォルダ内にコントローラのcsファイルを配置し、ビューは新しくViewフォルダを作成して更にその下にコントローラ名と同じフォルダを作成し、アクション名がファイル名となるようにcshtmlファイルを配置します。

今回はコントローラを`HomeController.cs`、ビューを`Index.cshtml`とします。

```csharp:HomeController.cs
public class HomeController : Controller
{
    public IActionResult Index()
    {
        // アクション名に一致するViewを返す
        return View();
    }
}
```

```html:Index.cshtml
<!DOCTYPE html>
<html>
<body>
    <h1>ASP.NET Core MVC</h1>
    <p>ASP.NET Core MVC最小構成のアプリケーションです。</p>
</body>
</html>
```

このまま実行して該当のURLにアクセスしても何も表示されません。
というのもASP.NET Core MVCはビューのファイルをプロジェクトディレクトリを基点として`/Views/[コントローラ名]/[アクション名].cshtml`で探しに行くため、プロジェクトディレクトリを明示的に設定する必要があるようです。
プロジェクトディレクトリの設定は`Program.cs`で行います。`Program.cs`ファイルを以下のように修正します。

```csharp:Program.cs
public class Program
{
    public static void Main(string[] args)
    {
        var builder = new WebHostBuilder()
            .UseKestrel()
            // 各ファイルの基点となるディレクトリを設定
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .Build();

        builder.Run();
    }
}
```

これでアプリケーションを実行して、`/Home/Index`にアクセスしてみると以下のように表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/39403/f896389a-70ed-e9df-752e-b969e91545dd.png)

# まとめ

先頭のTL;DRにまとまってます。