Microsoftは、2016年に最初にリリースされ、汎用、クロスプラットフォーム、オープンソースプラットフォームである.NET Core 2.0の次のメジャーバージョンをリリースした。.NET Coreは、現在の.NET Frameworkリリースで提供されている多くのAPIを使用できる。これはもともと次世代のASP.NETソリューションとして開発されたが、現在はIoT、クラウド、次世代のモバイルソリューションなど多くのシナリオの基礎になっている。このシリーズでは、従来の.NET開発者だけでなく、堅牢で実用的で経済的なソリューションを市場に投入する必要があるすべての技術者に.NET Coreのメリットをもたらす方法について説明する。
このInfoQの記事は、".NET Core"シリーズの一部である。RSSをサブスクライブすることで通知を受け取ることができる。
MicrosoftがWeb開発プラットフォームであるASP.NETを再構築したとき、彼らはIISと紐づけるのはいい案だとは考えなかった。実際にオリジナルのASP.NETはIIS固有の技術で構築されており、Windowsに結び付けられるだけでなく、自己ホストが不可能であり、新しいクラウド中心の世界ではうまくいかない。その代わりにMicrosoftは、Open Web Interface for .NET(OWIN)を組み込み、Webサーバーを完全に抽象化することに決めた。これによりフレームワークは、HTTPリクエストを受け入れる責任があるサーバーを考慮することなく、必要な機能の構築に集中できる。
OWINは新しいコンセプトではない。OWINの仕様は長い間、存在しており、MicrosoftはProject Katanaと呼ばれるオープンソースプロジェクトを通じて、IIS下で実行している間、開発者がそれを使用できるようにしている。実際のところ、Microsoftは開発者がKatanaを使うことを許可しただけでなく、数年にわたって全てのASP.NET認証機能の基盤になっている。
実際のところOWINとはなにか?正直に言ってそれは簡単だ!そしてシンプルであることはとても素晴らしい。これは、事前定義されたデリゲートと、文字列とオブジェクトの汎用的な辞書のみを使ったWebサーバーを抽象化するためのインターフェイスである。そのため、Webサーバーがイベントを発生させて、それにアタッチするイベントドリブンアーキテクチャではなく、ミドルウェアウェアと呼ばれるパイプラインを定義する。
ミドルウェアはクライアントからのリクエストに対応するコードの一部である(そう私は、意図的に「コードの一部」と言って非常に一般化していた)。 それは異なる2つの方法で実装できる。ほとんどのケースでクラスベースのバージョンが優先されていても、適切な署名を持つデリゲートが必要である。クライアントからリクエストが到着したときに、リクエストを検査し、クライアントに返されるレスポンスを変更することができる。もしそれを望まない場合、それ自身がクライアントに送信するレスポンスを生成することもできる。
ミドルウェアはパイプラインにまとめられ、パイプラインに追加された順番で、ミドルウェアからミドルウェアに渡され、ミドルウェアのいずれかがレスポンスを生成する方法を知っていて、決定するまでそれが続く。その後、クライアントに返す途中でミドルウェアによって変更される機能を持ち、検査されるパイプラインを通じてレスポンスが返される。非常にシンプルだが、柔軟で強力である。
このプロセスは、メッセージがパイプを流れていることが想像しやすいリクエストパイプラインと呼ばれ、それが流れたときにミドルウェアが対応する。ただし、パイプラインは、ミドルウェアからミドルウェアにメッセージを受け渡すときにパイプラインオブジェクトが必要になる。OWINでは、ミドルウェアがLinked listにしている。到着したリクエストは、リストの最初のミドルウェアに渡され、それが終わるまで次に渡され続ける。リクエストは、レスポンスが生成されて、それが再びリストに戻されるまで、ミドルウェアからミドルウェアに渡され、前のミドルウェアは少なくても高レベルにおいてメッセージを検査できる。実際には、レスポンスが生成されると、それがパイプラインを通る前であっても、クライアントにすぐに送信されるため、プロセスは少し複雑である。これにより、レスポンスを変更するのが少し複雑になるが、簡単にするためにそれは無視することにする。
OWIN仕様は文字列とオブジェクトのディクショナリとして受信リクエストが定義されている。サーバーは受信HTTPリクエストを受け付け、最初のミドルウェアが処理する前に、使用可能なピースに分解し、それらをディクショナリに配置する。リクエストパス、ヘッダーコレクション、本文を含むストリームなどを事前定義されたキーで辞書に追加する。これらの既知のキーを使用して、ミドルウェアは必要な情報をディクショナリから読み込んで、なにをする必要があるかを把握できる。サーバーはレスポンスストリームやレスポンスヘッダーコレクションなど、レスポンスに使うオブジェクトをディクショナリに追加することもでき、ミドルウェアがディクショナリ内のオブジェクトを使用して、クライアントに送信するレスポンスを生成することを許可するその上に、サーバーはディクショナリ内のデリゲートを提供することによって、ミドルウェアに追加機能をミドルウェアに公開できる。例えば、サーバーがクライアントにファイルを送信する特殊な方法を提供することを許可するOWIN仕様の拡張が存在する。これは、ディクショナリ内のオブジェクトを事前登録されたデリゲートとして提供することで、ミドルウェアに公開される。ミドルウェアはディクショナリを照会して、サーバーがその機能をサポートしているかどうかを確認し、サポートされている場合は、デリゲートを取得してファイルを送信することができる。
OWINインターフェイスはバカバカしいほどシンプルだが、非常に強力だ。これはHTTPリクエストとレスポンスの低レベルアクセスを提供するとともに、実際のリクエストを扱う際の詳細を抽象化する。ディクショナリ内のデリゲートによる高レベルの抽象化を提供する機能に制限がない。一方、それは非常に低レベルでありいくつかのことを面倒にしているのに、なぜMicrosoftがOWINを採用することを決めたのだろうか?
MicrosoftがASP.NET CoreにOWINを追加したときに、いくつかの変更が加えられた。彼らは、作業を少し楽にするために、その上にレイヤーを追加した(少なくても私にはそう見えた)。ミドルウェアにジェネリックディクショナリを提供するのではなく、より簡単に使えるように、型付レイヤを追加した。ディクショナリを追加する代わりに、より簡単にリクエストとレスポンスを操作できるように、HttpContextオブジェクトを取得できる。たとえば、クライアントへのレスポンスを書くために((Stream)dictionary[“owin.RequestBody”]).Write(…) と書く代わりに、httpContext.Response.Body.Write(…)と書くことができる。これは大きな違いではないが、正しいキーを思い出して、正しい方にキャストする必要がなくなり、エラーを起こしにくくできる。
Project Katanaでは、HttpContextはディクショナリ上にラップされ、その中の型付のアイテムにアクセスできる。ASP.NET Coreでは完全なカスタムオブジェクトである。しかし、OWINベースのミドルウェアを使いたいときのために「適切な」OWIN APIを提供する拡張機能がある。
本当にこのような低レベルAPIを使ってアプリケーションを構築しようと考えていますか?もちろん違います!これは、これまでにMicrosoftがやってきたことと全く同じで、低レベルAPIの上により高レベルの抽象化をして、簡単にしている。ASP.NET Coreでは、ASP.NET MVCがミドルウェアとして実装されている。.NET Coreの依存性注入機能を使ってミドルウェアに注入して、いくつかのサービスによってサポートされているが、結局はクライアントリクエストとのやり取りの責任を持つミドルウェアである。そしてこれは基本的で、低レベルAPIの可能性を提示する。
リクエストパイプラインに必要な数のミドルウェアを登録できるので、使いたいフレームワークと機能を混在させることができる。UIにASP.NET MVCを使いたい?心配ありません。MVCミドルウェアとサポートするサービスを追加するだけです。APIにNancyFxを使いたい?問題ありません。リクエストパイプラインにそれを追加するだけです。Facebook認証を使いたい?それも問題ありません!必要なミドルウェアとサービスを追加するだけです。APIのためにTwitter認証とJWT bearerトークンを組み合わせて使いたい?ミドルウェアを追加するだけです。 あなたがそれをできると信じています…このシンプルなインターフェイスは、サードパーティー製のミドルウェアと、カスタムで作ったものを組み合わせて、ニーズに完全にあったカスタムパイプラインを作ることができる。さらにリクエストを処理するミドルウェアがない場合は、なにもしない。例えば、静的ファイルミドルウェアをパイプラインに追加しなかった場合、ディスク上の静的ファイルは返されない。これは、IIS上にホストされたASP.NETとは完全に違い、ASP.NETの一部の機能と、IISの一部の機能が存在している。
ASP.NET Coreのリクエストパイプラインについての完全な記事を書くことはできず、どのように動作するかを示すためのコードは一行もない。例として認証を使ってみよう。これは最初にOWINとコンタクトをする最も一般的な方法の一つであり、インターフェイスが提供する素晴らしい例を提供するので、ここで使うのが最適である。
ASP.NET Core Webアプリケーションに認証を追加するには、アプリケーションに二つのことを追加する必要がある。まずサービスコレクションに認証サービスを追加する必要がある。そして、私たちがサポートしたい様々な種類の認証を伝える必要がある。
Facebookを使って認証をしたい場合、認証されると、私たちはCookie認証を使用してアプリケーションにサインインする必要がある。その設定は以下のようになる。
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(CookieAuthenticationDefautls.ApplicationScheme)
.AddFacebook(“### CLIENT ID ###”, “### CLIENT SECRET ###”)
.AddCookie();
}
サービスを導入したら、以下のようにリクエストパイプラインに認証ミドルウェアを追加する必要がある
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
// 認証済みユーザーを必要としないミドルウェア
app.UseAuthentication();
// 認証済みユーザーを必要とするミドルウェア
}
最後に、認証のためにFacebookにユーザーを送りした場合は、以下のように呼び出す
await HttpContext.ChallengeAsync(
new AuthenticationProperties { RedirectUri = "/userinfo" },
FacebookDefaults.AuthenticationScheme);
}
この呼び出しは、定義された認証スキームを使用して、ユーザーを正しいログインページにリダイレクトするためにHttpContextに設定する。この場合、Facebook認証ハンドラはリダイレクトレスポンスとしてステータスコードHTTP 302を返して、Uriへのlocationヘッダーは、Facebookにより認証されたクライアントを必要とするアドレスにリダイレクトする。ユーザーがFacebookによって認証されると、認証トークンとともにアプリケーションにリダイレクトされ、認証ミドルウェアはコールバックをキャッチし、Facebookサービスを使ってトークンを検証する。全てがOKになると、クライアントをサインインするために使用されるCookieを発行するようにCookie認証サービスに依頼する。実際には、Cookie認証サービスがHttpContextのレスポンスヘッダーのディクショナリにヘッダーを追加して、認証サービスが最終的にAuthenticationProperties.RedirectUriプロパティに定義されたクライアントの戻りパスにリダイレクトされるときにクライアントへのレスポンスに追加される。
したがって、いくつかのサービスとミドルウェアを登録するためにいくつかの拡張メソッドを使用して、認証が要求されたときにユーザーをリダイレクトすることと、認証されたときにアイデンティティプロバイダからのコールバックを処理することの両方をする、かなり複雑な認証フローを設定した。
注意:前の設定では、実際にはユーザーがチャレンジするときに、デフォルトでFacebookの代わりにCookie認証をつかってユーザーを認証しようとする。ちょっとした設定変更で、Facebookをデフォルトで使用できるが、この記事のスコープを少し超えている。
私はASP.NET CoreがOWINベースのパイプラインに移行することは素晴らしいことだと信じている。これは非常にシンプルで、低レベルのインターフェイスかもしれないが、正しい方法で使用すると素晴らしいものである。シンプルなインターフェイスを作成し、低レベルと高レベルの両方の使用を可能にしていると同時に、将来できることを制限しないことは簡単ではない。だが、これはそれをできていると信じている。少しクリエイティブなことをしようとするとちょっとした制限が存在する--それはあなたが解決する必要があるだけの問題である。
著者について
Chris Klugはスウェーデンのストックホルムにあるtretton37のソフトウェア開発者兼アーキテクトである。彼はソフトウェアを書くことで問題を解決して、人生の一部をよくすることに多くの時間を費やしてきており、彼は継続的に提供されるチャレンジとしてのコーディングのクリエイティブな側面を愛している。彼はまた、世界中の開発者カンファレンスにおけるプレゼンテーションに非常に多くの時間を費やしている--7年間Microsoft MVPを受賞していることからも、Microsoftの注目がうかがえる。しかし、彼がコンピューターで遊ぶよりも、素敵なビーチでカイトボーディングをしたり、タトゥーを増やすことが好きであることを認めても問題ない。
Microsoftは、2016年に最初にリリースされ、汎用、クロスプラットフォーム、オープンソースプラットフォームである.NET Core 2.0の次のメジャーバージョンをリリースした。.NET Coreは、現在の.NET Frameworkリリースで提供されている多くのAPIを使用できる。これはもともと次世代のASP.NETソリューションとして開発されたが、現在はIoT、クラウド、次世代のモバイルソリューションなど多くのシナリオの基礎になっている。このシリーズでは、従来の.NET開発者だけでなく、堅牢で実用的で経済的なソリューションを市場に投入する必要があるすべての技術者に.NET Coreのメリットをもたらす方法について説明する。
このInfoQの記事は、".NET Core"シリーズの一部である。RSSをサブスクライブすることで通知を受け取ることができる。