May 2017
Volume 32 Number 5
Cutting Edge - ASP.NET 開発者向けの ASP.NET Core
Dino Esposito | May 2017
ASP.NET Core についての話題の中心は、マルチプラットフォーム エクスペリエンスが実現されることです。これは間違いなく大きな成果ですが、一般的な ASP.NET ユーザーには必ずしもプラスになるわけではありません。一般的なユーザーは、.NET 4.x コードの大規模なコードベースに取り組み、使い慣れた IIS と Windows の環境から離れる予定はありません。だとしたら、このような一般的 ASP.NET 開発者にとっての ASP.NET Core の価値は何でしょう。
この新しいプラットフォームは、最初まったく異なって見えるかもしれません。まるで、ひと晩のうちにだれかがチーズをこっそりどこかに移動させたように感じます。ASP.NET Core は、最新のプラクティスに従って、根本から新しく作り直されています。そのため、プログラミング能力や顧客の懸案事項に対処する能力が向上する可能性がありますが、そうはならない場合もあります。この疑問を実際に解決できるのは、他のだれでもなく、開発者自身です。今回は、大げさな表現は避けます。また評価の基準を示すことも、なんらかのテクノロジに注目することもしません。物事の本質を明らかにすることに取り組みます。プラットフォームの現状に満足している開発者が関心を持ちそうな ASP.NET Core の側面を明らかにします。
フレームワークでエンジニアリングされた一般的なプラクティス
ASP.NET チームのメンバーが最初の ASP.NET フレームワークを設計したときは、Active Server Pages のベスト プラクティスを最大限に生かし、新しいフレームワークのエンジニアリングに取り込みました。その際、コンパイル済みのマネージ コード、自動ポストバック、サーバー コントロールなど、新しい概念も数多く導入しています。ASP.NET Core の進化パターンも同じです。
構成データの初期読み込み、依存関係の注入、NuGet パッケージ、クレームベースの認証、Razor の改善など、一般的な開発プラクティスが、この新しいフレームワークのネイティブ機能として取り込まれています。この新しいフレームワークは、スタートアップの手続きが異なり、これまで以上にモジュール化を進めた要求/応答型のミドルウェアです。それでも、コントローラーやビューを定義する場合の柔軟性が向上したインフラストラクチャになっています。ASP.NET Core もまた、クロスプラットフォーム フレームワークなので、Windows だけでなく、macOS や Linux でもアプリケーションを開発して、ホストすることができます。ある意味、ASP.NET Core では、やや高いレベルでの懸念事項の分離が既定で求められるため、このような状況に適したコードを作成しなければなりません。ただし、このようなコーディングは訓練次第で実現できます。
どのような形式のグリーンフィールド開発でも、ASP.NET Core は最適な選択肢になります。とは言え、新しいフレームワークなので、初期コストは避けられません。 チーム全員がこのフレームワークに慣れなければなりません。さらに、全員がモデル - ビュー - コントローラー (MVC: Model-View-Controller) アプリケーション モデルに詳しいか、詳しくなる必要があります。グリーンフィールド開発に関わるすべての要素がまったく新しくなったわけではありません。既存のコード チャンク、少なくとも、既存のスキル (データ アクセスやセキュリティのスキル) の再利用が求められます。こうした再利用は現実的にどの程度可能なのでしょう。 この点に対処するため、ASP.NET Core には 2 つの選択肢が用意されています。
ASP.NET Core の選択肢
図 1 は、新しいプロジェクトを作成するための Visual Studio 2015 のダイアログ ボックスです (基本的には Visual Studio 2017 でも同じです)。
図 1 Visual Studio での新しい ASP.NET Core プロジェクトの作成
最初のテンプレートは、Core を使用しない従来のプロジェクトを作成します。他の 2 つのテンプレートは、異なる .NET Framework を対象に ASP.NET Core プロジェクトを作成します。ここが ASP.NET Core の未知の領域を探る場合の最初の分岐点です。
完全版の NET Framework を選択すると、既存のすべての .NET クラス ライブラリにアクセスできますが、ホスティングは Windows と IIS に限定されます。図 2 に 2 つの選択肢の違いをまとめます。
図 2 ASP.NET Core の選択肢の基本的な違い
フレームワーク | 違い |
.NET Framework | ASP.NET MVC のみ、WebForms なし 新しいランタイム環境とプログラミング API 選択した .NET Framework バージョンをターゲットにするすべてのライブラリ IIS ホスティングのみ |
.NET Core | ASP.NET MVC のみ、WebForms なし 新しいランタイム環境とプログラミング API .NET Core ライブラリのみ クロスプラットフォーム ホスティング |
どちらのフレームワークを選択しても、ASP.NET Core プロジェクトのコードは新しいランタイム環境に公開されます。この新しいランタイム環境では、system.web に基づく MVC ランタイムと、OWIN の原則を尊重する Web API ランタイムが統一されます。
IIS との決別
近年、Web API フレームワークは、HTTP 対応のすべてのクライアントに RESTful インターフェイスを公開するというシン サーバー機能への大きなニーズに対処しようとしています。Web API は Web サーバーからアプリケーション モデルを切り離し、OWIN 規格 (Web サーバーとアプリケーションを相互運用する場合の一連の規則) へと変化しています。ただし、Web API はホストを必要とし、ASP.NET アプリケーションのコンテキストでホストされる場合、メモリのフットプリントに別のランタイム環境を追加するだけです。これは、業界が非常に単純な Web に移行するのと同じタイミングで起こっています。非常に単純で最低限の機能しか持たない Web サーバーとは、できる限り高速にコンテンツを取得する HTTP エンドポイントです。つまり、なんらかのビジネス ロジックを覆う薄い HTML 層にすぎません。非常に単純なサーバーが行う必要があるのは、要求を適宜処理し、ビジネス ロジック以外のオーバーヘッドを発生させずに、応答を返すことです。
現在の ASP.NET ランタイム環境は、ある程度のカスタマイズは可能ですが、このようなシナリオに対処するように設計されてはいません。ホスティング環境と ASP.NET 環境の分離は、ASP.NET Core に見られる大きな変化で、これによりアプリケーションレベルの変更が数多く必要になります。
アプリケーションのスタートアップ
新しいプロジェクトを作成後最初に気付くのは、global.asax ファイルがなくなり、program.cs ファイルが存在するようになったことです。少し驚きますが、ASP.NET Core アプリケーションは、dotnet ドライバー ツール (bit.ly/2mLyHxe、英語) によって起動される、プレーンなコンソール アプリケーションです。
この dotnet ツールが、マルチプラットフォーム サポートの鍵になります。新しいプラットフォーム向けにコマンドライン ツール (および.NET Core フレームワーク) が利用できるようになったら、ホスティングの役割はこうしたツールに Web サーバーを接続することだけに縮小されます。IIS での発行は、アドホック モジュールである .NET Core Windows Server Hosting パッケージ (bit.ly/2i9cF4d) によって行われます。Apache での公開は、Ubuntu サーバーで構成ファイルによって行われます。bit.ly/2lSd0aF (英語) で例を参照してください。
実際の Web サーバーはリバース プロキシとして機能し、構成済みのポートを経由してコンソール アプリケーションと通信します。コンソール アプリケーションは、もう 1 つの単純な Web サーバーを覆うように構築されます。このWeb サーバーは要求を受け取り、内部アプリケーション パイプラインをトリガーして、その要求を処理します。この処理を行うのが以下のコードです。
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
Host.Run();
Kestrel が ASP.NET Web サーバーの名前です。このサーバーが着信要求を受け取り、パイプラインを介してこの要求を処理します。コード スニペットの IISIntegration モジュール呼び出しは、IIS でホストする場合にのみ必要です。
Kestrel 内部 Web サーバーには (現時点では) 分散型サービス拒否 (DDoS) 攻撃などを防ぐフィルターが含まれていないため、主にセキュリティ上の理由から、ASP.NET Core アプリケーションを囲むリバース プロキシを用意することをお勧めします。純粋に機能の点では、リバース プロキシを必ず有効にしなければならないというわけではありません。
既に述べたように、ASP.NET Core では global.asax ファイルがなくなり、web.config の役割が完全に失われました。実際のところ、global.asax ファイルの目的は、アプリケーションの代わりに IIS が静的エラー ページを提供するなど、いくつかの処理を行えるようにすることだけです。エラー処理の構成、ログ記録、認証、グローバル構成データの格納などの主要機能は、startup クラスから編成される新しい API を利用して行われます。
Startup クラス
startup クラスには、少なくとも初期化フェーズ中にホストが呼び出すメソッドがいくつか含まれます。
public class Startup
{
public void ConfigureServices(IServiceCollection services)
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync(DateTime.Now)
});
}
}
ConfigureServices メソッドを使って、アプリケーションが使用するシステム サービスを宣言します。技術的には、このメソッドは省略可能ですが、実際のシナリオではどのような場合でも 1 つは必要になるでしょう。従来の ASP.NET 開発者は、MVC アプリケーション モデルの使用さえ明示的に宣言して有効にしなければならないことにショックを感じるかもしれません。ただし、このことは ASP.NET Core でモジュール化の採用が進んでいることを示しています。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
Configure メソッドでは、以前に要求したサービスをすべて構成します。たとえば、ASP.NET MVC サービスを要求した場合、Configure ではサポートするルートのリストを指定できます。内部 Web サーバーが jQuery や Bootstrap などの共通ファイルを含む静的ファイルを提供できるようにする場合も、明示的な呼び出しが必要です。
public void Configure(IServiceCollection services)
{
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
...
}
startup クラスは、アプリケーションのミドルウェアを構成する場所でもあります。ミドルウェアは新しい用語ですが、概念的には現在の ASP.NET の HTTP モジュールと大幅に重複しています。ASP.NET Core では、ミドルウェアが図 3 に示すように機能します。
図 3 ASP.NET Core のミドルウェア
着信する任意の要求を事前および事後に処理する機会を提供するコード ブロックをミドルウェアに登録することができます。つまり、各ミドルウェアは終了の前後に実行されるコード (startup クラスの Configure メソッドにおける Run メソッド) を登録できます。全体としてのモデルは、IIS の以前の ISAPI モデルに似ています。ミドルウェアの例を以下に示します。
app.Use(async (httpContext, next) =>
{
// Pre-process the request
// Yield to the next middleware
await next();
// Post-process the request
});
ミドルウェアは、HttpContext オブジェクトを受け取り、Task を返す関数です。ミドルウェア コンポーネントのリストは、Run メソッドで終わります。現在の ASP.NET パイプラインと比べると、ASP.NET Core パイプラインは双方向で、完全にカスタマイズ可能です。さらに、既定では空になっています。
非常に単純な Web サービス
当然、パイプラインに Run メソッドがなければ、どの要求からも応答が生成されません。言い換えれば、応答を生成するのに必要なのは Run メソッドだけです。これは、ASP.NET Core アプリケーションのパイプラインが短くなることを示しています。ASP.NET WebForms や MVC では、要求ごとに独自のコードが実行される前に多くのことが行われます。たとえば、コントローラー メソッドの解決は、アクションの呼び出し元を中心とするサブシステム全体に関わる非常に長い手続きになります。一方 Run メソッドは、要求の直後に続く直接的な呼び出しです。
画像ファイル (フラグなど) のリストを所有し、入力パラメーターまたはデバイスのプロパティに基づいて適切なサイズの画像を返すファイル サーバーを作成するとします。現在の ASP.NET で最も高速な選択肢は、おそらくアドホックの HTTP ハンドラーを作成することです。HTTP ハンドラーとは、固定 URL ルートにマップされる IHttpHandler インターフェイスを実装するクラスです。HTTP ハンドラーはパイプラインが簡潔になるため、ASPX エンドポイントと MVC コントローラー アクションを利用するよりも高速になります。また、HTTP ハンドラーは、基本 ASP.NET パイプラインの上位に 2 つ目の OWIN パイプラインを必要としないため、Web API エンドポイントよりもフットプリントが少なくなります (これは、IIS 外でホストされる Web API ソリューションには当てはまりません)。
ASP.NET Core では、効果的なファイル サーバーの作成が、これまで以上に容易かつ効率的になります。必要なのは、追加のロジック (サイズ変更と取得) を設計し、 ConfigureServices メソッドの Run メソッドにバインドすることだけです。
public void Configure(IApplicationBuilder app)
{
app.Run(async (context) =>
{
var code = context.Request.Query["c"];
var size = context.Request.Query["s"];
var file = FindAndResizeFlag(code, file);
await context.Response.SendFileAsync(file);
});
}
上記の例では、指定されたパラメーターと一致するサーバー ファイル名を探すカスタム ロジックがあると仮定して、Response オブジェクトを使用してサーバーファイル名を呼び出し元に返しています。これ以外のコード (表示や非表示のコード) は必要ありません。率直に言うと、これ以上簡単な方法はありません。
ASP.NET Core についての感想がどうであれ、たとえ懐疑的でも夢中でも、ASP.NET Core は他の ASP.NET プラットフォームにはない、非常に単純で最低限の機能を備えた Web サービスの作成という特有の機能を提供します。非常に単純な Web サービスの構築を可能にするインフラストラクチャは、同時にどのような要求でも合理的に最低限のオーバーヘッドで処理できる最高の保証になります。
まとめ
生産性の高い ASP.NET Core 開発者になるために理解する必要があるのは、WebForms よりも新しいアプリケーション モデル (ASP.NET MVC や単一ページ アプリケーション モデル) だけです。外観も大きく変わっていますが、ASP.NET Core の大きな変更点のほとんどは、ランタイム環境に関するものです。ホスティング モデルとミドルウェアを理解すれば、それだけで新しいプラットフォームがわかります。結局、中心になるのはコントローラーの作成と Razor ビューのレンダリングです。認証、ログ記録、構成などのいくつかの共通操作では、それぞれ異なる API が必要になりますが、そのような API を学ぶのに時間はかかりません。難しいのは、自身にとってメリットになるものを探すことです。
Dino Esposito は、『Microsoft .NET: Architecting Applications for the Enterprise』(Microsoft Press、2014 年) および『Modern Web Applications』(Microsoft Press、2016 年) の著者です。JetBrains の .NET および Android プラットフォームのテクニカル エバンジェリストでもあります。世界各国で開催される業界のイベントで頻繁に講演しており、software2cents.wordpress.com (英語) や Twitter (@despos、英語) でソフトウェアに関するビジョンを紹介しています。
この記事のレビューに協力してくれたマイクロソフト技術スタッフの James McCaffrey に心より感謝いたします。