ASP.NET Core RC2でHelloWorld~EntryPointとStartupも探る
ASP.NET Core RC2 と Visual Studio 2015 UPDATE 2でHello Worldを作ってみようと思います。 で、その後でASP.NET Coreの基本動作の部分について少し探索してみようかと思います。
まずは準備
①Visual Studio 2015:UPDATE 2を適用した環境を用意
②.NET Core RC2をインストール
「https://www.microsoft.com/net」にアクセスして.「Download NET Core」をクリック
「Visual Studio official MSI Installer」をクリックしてダウンロードしたインストーラを実行。
「NuGet Manager extension for Visual Studio」をクリックしてダウンロードしたインストーラを実行。
これでASP.NET Core RC2の開発を行う環境が整いました!
Hello Worldの作成
①Visual Studio 2015を起動し「ファイル→新規作成→プロジェクト」メニューを選択
テンプレート:Web→ASP.NET Core Web Application(.NET Core) 名前:HelloAspNetCore
とします。
空を選択します。
以下のようなソリューション・プロジェクトが作成されました。
②実行
Hello World!が完成しました。
実装の確認
例にもれず最近の開発環境は自動生成コードでHello World(もしくはもっとリッチ)な実装を生成してくれます。 と、いうことでざっくり実装を見ていこうと思います。
ポイントは・・・ Program.csに実装されたエントリーポイント Startup.cs実装されたStartupクラス
エントリーポイント
従来のASP.NETのエントリーポイントは不明確(?)でありましたが、ASP.NET Coreでは、コンソールアプリケーションと同様に明確です。この点については、新人教育等で論理的にプログラムの動きを説明するにはASP.NET Coreの方が分かりやすいでしょう。
従来のコンソールアプリケーションと同様に「staticなMain()メソッド」がエントリーポイントとなります。 例えば、プロジェクトにClass.csを追加して、ここにも同様にpublic static void Main(string[] arg)メソッドを追加すると以下のようなビルドエラーとなります。複数のエントリーポイントがありますよ!と、/mainでエントリーポイントを含むタイプを指定しろ、と。まあ、通常複数のMain()を定義することはありませんね。
そしてProgram.csの実装が以下になります。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; namespace HelloAspNetCore { public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); } } }
WebHostBuilderクラスを利用し「このWebアプリケーションをホストする構成」を生成します。host変数は「IWebHostインターフェイス型」になります。 WebHostBuilderクラスの生成と同時にいくつかのメソッドを呼び出していますが、これらによって必要な構成を決定します。
UseKestrel()
Kestrelと呼ばれるアプリケーションサーバーを使用することを示します。ASP.NET Coreはマルチプラットフォーム対応であるため、IISなどのWebサーバーとアプリケーションサーバーを分離した構成をとります。IIS等のWebサーバーはリバースプロキシの役割を果たし、Kestrelのようなアプリケーションサーバーでロジックが動作します。UseContentRoot(Directory.GetCurrentDirectory())
コンテンツルートをカレントディレクトリとします。UseIISIntegration()
IIS統合で動作する構成とします。UseStartup
()
スタートアップ処理を実装したクラスとして「Startup」(厳密にはこのプロジェクトに実装されたHelloAspNetCore.Startupクラス)を使用するよう構成します。
上記3つのメソッドはWebHostBuilderの拡張メソッドであり、IWebHostBuilder型オブジェクトを返却します。 例えばUseKestrel()はMicrosoft.AspNetCore.Server.Kestrelアセンブリに、UseIISIntegration()はMicrosoft.AspNetCore.Server.IISIntegrationアセンブリに実装されています。
- Build()
設定した構成をビルドします。戻り値の型はIWebHostとなります。
そしてRun()の呼び出しで、設定した構成のWebホストを実行します。
Startupクラス
実装は以下の通りです。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace HelloAspNetCore { public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app) { app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
Main()メソッド内で「UseStartup
public void ConfigureServices(IServiceCollection services)
public void Configure(IApplicationBuilder app)
ConfigureServices()は、サービスの構成の初期化になります。 例えば、ASP.NET CoreはDIをベースとしていますが、コントローラークラスから使用するサービスクラスのDI登録等を行うことになります。
Configure()は、HTTPリクエストパイプラインの構成を実装します。 自動生成されたコードでは、何がリクエストされても常にHello World!の文字をレスポンスする実装となっています。 つまり「http://localhost:24912/」とリクエストしても「http://localhost:24912/こんばんは」とリクエストしても常に「Hello World!」とブラウザには表示されます。
ConfigureServices() / Configure()とEnvironmentName
スタートアップクラスのASP.NET Coreのランタイム内でConfigureServices()とConfigure()が呼びだされますが少し不思議ですよね。特定のインターフェイスを実装したクラスでもありません。つまり、ランタイム内部ではUseStartup
というか実際にオープンソースの「Microsoft.AspNetCore.Hosting\Internal\StartupLoader.cs」あたりを覗くとMethodInfoを使ったリフレクションで処理が記述されているのが分かります。
さらにStartupLoader.csを除いていると以下のような実装が・・・
private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName) { var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true); return new ConfigureBuilder(configureMethod); } private static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName) { var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false) ?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false); return servicesMethod == null ? null : new ConfigureServicesBuilder(servicesMethod); }
ConfigureServices() / Configure()の両メソッドの名称を「Configure{0}, environmentName」「Configure{0}Service, environmentName」としています。
ということで、Startup.csの実装を以下のように修正してみましょう。 ConfigureDevelopmentServices()とConfigureDevelopment()を追加しました。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace HelloAspNetCore { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } // >> 追加 public void ConfigureDevelopmentServices(IServiceCollection services) { } public void ConfigureDevelopment(IApplicationBuilder app) { app.Run(async (context) => { await context.Response.WriteAsync("Hello World!(Development Environment)"); }); } // << 追加 } }
また、environmentName(環境変数)とはVS2015でプロジェクトのプロパティの以下の値を表します。 「Development」となっています。
HelloAspNetCoreを実行してみましょう!
はい。Startup.ConfigureDevelopment()が呼び出されたことが分かります。
ちなみに、環境変数「ASPNETCORE_ENVIRONMENT」を削除して実行するとStartup.Configure()が呼び出される動作に戻ります。