ASP.NET vNext で最低限必要な構成を調べてみた。


そこらへんで話題のASP.NET vNextですが、Microsoftがいい感じで振り切ってくれて今までの知識がいろいろひっくり返りました。(Owinやってたらある程度は延長線上なのですが)
Visual Studio 2015 previewも出てて、プロジェクトも作れて、F5で一発で動くのですが、いかんせんデフォルトのテンプレートが物が入りすぎてる or 中途半端に何もない。Macを使う場合はどうなのよ。ってのがよく分からんので、まずは最低限必要な設定を調べてみました。

長いので、結果だけ知りたい方は[最小の構成](#最小の構成)をチェック。

まずは環境構築
------------
Visual Studioは甘えってことで今回はコンソール&エディタでやっていきます。で、まずは環境ですが、これは以下を参照。

[ASP.NET/home](https://github.com/aspnet/home#getting-started)

Windowsでは.NETを入れて、Powershell叩いたら終わりですね。たぶんこれはめっちゃ簡単。Macの場合はHomebrew入れて、Mono入れて、何個かコマンド叩く感じです。個人的にMac初心者でここらへんいろんなサイト見ながら頑張ったんで、もう一回やれと言われてもすぐにできる自信なし。なので、説明書く自信もなし。
まぁ、でも上のサイト見ながら手順やってたら大丈夫なはず。とりあえずコンソールで"k"って叩いて以下のようなのが出たらOK。これはWindowsでもmacでもおなじ。

```
Usage: k [options] [command]

Options:
  --watch                          Watch file changes
  --packages <PACKAGE_DIR>         Directory containing packages
  --configuration <CONFIGURATION>  The configuration to run under
  --port <PORT>                    The port to the compilation server
  -?|-h|--help                     Show help information
  --version                        Show version information

Commands:
  run  Run application

```

調べてみる
--------
最低限の構成って？ってのを調べたいので、とりあえずHello Worldのサンプルあたりが一番小さそうなため、それを潰していきます。

[HelloWeb](https://github.com/aspnet/Home/tree/master/samples/HelloWeb)

これをみると、Startup.csとimage.jpg、project.jsonってファイルが入ってるんやけど、image.jpgはまず間違いなくいらないと思われるので、Startup.csとproject.jsonだけみればOKなはず。

project.jsonの中をみると、どうも設定系の内容っぽい。とりあえず転記。

```json
{
    "dependencies": {
        "Kestrel": "1.0.0-beta1",
        "Microsoft.AspNet.Diagnostics": "1.0.0-beta1",
        "Microsoft.AspNet.Hosting": "1.0.0-beta1",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta1",
        "Microsoft.AspNet.StaticFiles": "1.0.0-beta1"
    },
    "commands": {
        "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5001",
        "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5004"
    },
    "frameworks": {
	    "aspnet50": {},
	    "aspnetcore50": {}
	}
}
```

dependenciesは使うアセンブリの設定。今までで言う所の参照設定ですかね。

commandsは環境つくるときに参照した[ASP.NET/home](https://github.com/aspnet/home#getting-started)に[Samples](https://github.com/aspnet/home#samples)ってのがあって、その中のRunning the samplesを読むと、windowsでは

```
k web
```

monoでは

```
k kestrel
```

を叩けって書いてあって、それぞれのパラメーターの値が定義と一致してるので、kコマンドのパラメーター定義ですね。Microsoft.AspNet.Hostingってアセンブリ使って、HttpServerはどれ使えってのと、その時のurlはこうしろってのが書かれてます。

frameworksってのは謎です。aspnet50とaspnetcore50ってのがどう違うかも謎。これの詳細は今後の課題。

とりあえず、project.jsonだけ作ってみます。

```json
{
    "dependencies": {
        "kestrel": "1.0.0-*",
        "Microsoft.AspNet.Hosting": "1.0.0-beta1",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta1"
    },
    "commands": {
        "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5001",
        "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5001"
    },
    "frameworks": {}
}
```

dependenciesのアセンブリの定義はcommandsで使ってるものだけに絞ってます。で、そのままコマンドを叩いてみると、以下のように怒られました。

```
System.InvalidOperationException: Failed to resolve the following dependencies f
'Asp.Net,Version=v5.0':
   kestrel 1.0.0-*
   Microsoft.AspNet.Hosting 1.0.0-beta1
   Microsoft.AspNet.Server.WebListener 1.0.0-beta1

Searched Locations:
  C:\work\Local\vNext1\{name}\project.json
  C:\Users\user\.kpm\packages\{name}\{version}\{name}.nuspec
  C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\
  C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\
}.dll
  C:\Windows\Microsoft.NET\assembly\GAC_32\{name}\{version}\{name}.dll
  C:\Windows\Microsoft.NET\assembly\GAC_MSIL\{name}\{version}\{name}.dll

Try running 'kpm restore'.

   場所 Microsoft.Framework.Runtime.DefaultHost.GetEntryPoint(String application
   場所 Microsoft.Framework.ApplicationHost.Program.ExecuteMain(DefaultHost host
Name, String[] args)
   場所 Microsoft.Framework.ApplicationHost.Program.Main(String[] args)
```

macの場合は長いので割愛しますが、とりあえず読み解くと、アセンブリ見つからんから"kpm restore"ってコマンド叩けって怒ってるようです。

ってことで、気をとりなおして

```
kpm restore
```

すると、nuget.orgからいろいろダウンロードされて配置されます。dependenciesには3つしか書いてないのですが結構落ちてきますね。たぶんこれはVisual Studioだと勝手にやってくれるんでしょうけど、そうじゃない場合は手動でやる必要がありそうです。リポジトリから新しいソースとったらとりあえず叩いとけってところでしょうか。

再び、`k web` or `k kestrel`を叩くと再び怒られる。

```
System.Exception: TODO: StartupDevelopment or Startup class not found in assembl
   場所 Microsoft.AspNet.Hosting.Startup.StartupLoader.LoadStartup(String applic
vironmentName, IList`1 diagnosticMessages)
   場所 Microsoft.AspNet.Hosting.Startup.StartupManager.LoadStartup(String appli
nvironmentName)
   場所 Microsoft.AspNet.Hosting.HostingEngine.EnsureApplicationStartup(HostingC
   場所 Microsoft.AspNet.Hosting.HostingEngine.EnsureApplicationDelegate(Hosting
   場所 Microsoft.AspNet.Hosting.HostingEngine.Start(HostingContext context)
   場所 Microsoft.AspNet.Hosting.Program.Main(String[] args)
```

またしてもmac(というかmono)は長いので割愛しますが、StartupDevelopmentかStartupクラスがないよって怒ってます。アプリケーションのエントリーポイントが見つからんよって怒ってるんでしょうね。確かに、サンプルにはStartup.csってのがありました。

で、Startup.csを作って動かしてみます。ただし、サンプルをそのままではなく、定義はクラスのみにしてみます。

```csharp
namespace Test {
    public class Startup {

    }
}
```

で、動かしてやはり怒られてみます。まぁ、これは予想通りですが。

```
System.Exception: TODO: ConfigureDevelopment or Configure method not found
   場所 Microsoft.AspNet.Hosting.Startup.StartupLoader.FindMethod(Type startupTy
e, String environmentName, Type returnType, Boolean required)
   場所 Microsoft.AspNet.Hosting.Startup.StartupLoader.LoadStartup(String applic
vironmentName, IList`1 diagnosticMessages)
   場所 Microsoft.AspNet.Hosting.Startup.StartupManager.LoadStartup(String appli
nvironmentName)
   場所 Microsoft.AspNet.Hosting.HostingEngine.EnsureApplicationStartup(HostingC
   場所 Microsoft.AspNet.Hosting.HostingEngine.EnsureApplicationDelegate(Hosting
   場所 Microsoft.AspNet.Hosting.HostingEngine.Start(HostingContext context)
   場所 Microsoft.AspNet.Hosting.Program.Main(String[] args)
```

次は、ConfigureDevelopmentまたはConfigureメソッドがないって怒られてます。ってことはStartupクラスは認識できて、次のステップに移ったってことですね。

ってことで、Configureメソッドを定義してみます。で、サンプルにはConfigureメソッドにはIApplicationBuilderの引数があるのですが、あえて引数なしメソッドにしてみます。

```csharp
namespace Test {
    public class Startup {
        public void Configure() {
        }
    }
}
```

コマンドを叩くと、、、

```
C:\work\Local\vNext1\src>k web
Started
```

おっ、動いた。

最小の構成
----------
と、いうことで必要なファイルは
 
* project.json
* Startup.cs

の2種類で、それぞれは以下のような感じ(再掲)

```json:project.json
{
    "dependencies": {
        "kestrel": "1.0.0-*",
        "Microsoft.AspNet.Hosting": "1.0.0-beta1",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta1"
    },
    "commands": {
        "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5001",
        "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5001"
    },
    "frameworks": {}
}
```

```csharp:Startup.cs
namespace Test {
    public class Startup {
        public void Configure() {
        }
    }
}
```

まぁ、サーバーが動いただけでリクエストを受け付ける口が何もないのでアクセスしても真っ白な状態ですが。。。

とりあえず、何か返すようにする
------------------------
さてさて、何か返さないとただのリソースの無駄遣いなので何か返すようにします。で、ダラダラ書いてきて結構疲れたのでここからは一気に。Startup.csを以下のようにします。

```csharp:Startup.cs
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;

namespace Test {

    public class Startup {

        public void Configure(IApplicationBuilder app) {

            app.Use((context, next) => {
                return context.Response.WriteAsync("Hello World");
            });

        }
    }
}

```

これで、ブラウザで http://localhost:5001 にアクセスするとHello Worldが表示されます。

ConfigureメソッドのIApplicationBuilderには、Microsoft.AspNet.Builder.ApplicationBuilderのインスタンスが入ってきます。これはDIで放り込まれてるのですが、じゃ、実際のインスタンスがどのタイミングでどういう指定で生成されてるかは要調査。DIのコードは[この辺](https://github.com/aspnet/DependencyInjection)ですね。

で、IApplicationBuilderのUseメソッドはHttpのパイプラインで実行される処理の記述です。ここでレスポンスのボディにHello Worldって書き込んで終わらしてます。

サンプルには

* app.UseStaticFiles()
* app.UseWelcomePage()

ってのがありますが、これはちょうどproject.jsonのdependenciesの定義で外した

* Microsoft.AspNet.StaticFiles
* Microsoft.AspNet.Diagnostics

のアセンブリに拡張メソッドで定義されてて、静的ファイルの処理とルートにアクセスした時にようこそ画面を表示する処理をします。これらはMiddlewareって呼ばれてて、ざっくりというとHTTPのリクエストに対して処理して、レスポンスをつくる役割をおってます。このMiddlewareが次々に呼ばれて処理をするって構成ですね。なので、上に書いたHello Worldを返してるラムダもインラインでのMiddleware定義になりますね。

実際は、Useメソッドを直接使う事はほとんどなくって、用意されているMiddlewareを使うことになるでしょう。なのでMVCとかSignalRとかとかもMiddlewareで用意されててapp.UseMvc()とかって感じで使います。UseXXXとかっていっぱい書くけど、実際はレスポンスに書き込んでるだけよってことで書いてみました。っていうか、nodejsとかやったらこんな感じの説明がまず最初にあるのをよく見かけたのですが、こと今回のASP.NET vNextは、上のようなUseを直接使ってる例はまだ見たことがないので、そこはかとなく文化の違いを感じるところです。