ASP.NET 5 で Azure API App

ASP.NET 5 はまだプレビュー、Azure API App もプレビューなので、Visual Studio にはそれらを組み合わせたプロジェクト テンプレートが用意されていません。
今回、いくつかのエラーに阻まれながらも、試行錯誤して、なんとか動かすことができたので、手順を報告したいと思います。
ただ ASP.NET 5 の Web API を動かすだけであれば、普通に Azure API App にデプロイすれば一応動くのですが、API App としてはいくつかのお約束事がありますので、その辺りを中心に解説します。*1

なお、開発環境は、Visual Studio 2015 上に ASP.NET 5 Beta7 の開発環境を整えた状態になっています。
とりあえず最新の ASP.NET 開発ツールはインストールしておいてください。

サンプル コードはこちらです。適宜参照しながらお読みください。github.com

Azure API App を作る

Azure ポータルにサインインし、【新規】→【Web + モバイル】→【API App】でアプリケーションを作ります。
お金はかけたくないので、料金プランは【すべて表示】をクリックして【F1 Free】を選択しました。
f:id:aetos382:20150830023720p:plain:w400
f:id:aetos382:20150830023725p:plain:w400
f:id:aetos382:20150830023729p:plain:w400

アプリケーションの準備ができるまでしばらく待ちましょう。

完了したら、ポータルから今作ったアプリケーションを開き、【すべての設定】→【Application settngs】を選択して、【Access Level】を【Public (anonymous)】にしておきます。
こうしておかないと動作確認ができません。*2
f:id:aetos382:20150830024516p:plain:w400

また、「API app host」をクリックして API App のホストとなる Web App を開き、発行プロファイルをダウンロードしておきます。
今のところ Visual Studio が未対応なためか、VS から API App に直接デプロイすることができないためです。
f:id:aetos382:20150915201739p:plain:w400

プロジェクトを作る

ASP.NET Web アプリケーション」を選択し、ASP.NET 5 プレビュー テンプレートの【Web API】でプロジェクトを作ります。
f:id:aetos382:20150901011604p:plain:w400
f:id:aetos382:20150901011657p:plain:w400

それからもう一つ、お手本にするために、ASP.NET 4.6 の Azure API Apps のプロジェクトも作っておきます。
f:id:aetos382:20150901011706p:plain:w400
f:id:aetos382:20150901011711p:plain:w400

そうしたら、ASP.NET 4.6 の方から ASP.NET 5 の方へ、以下のファイルとフォルダーをコピーしましょう。

  • Metadata フォルダー
  • apiapp.json ファイル

次に、ASP.NET 5 の方に、Swashbuckle をインストールします。
【プレリリースを含める】をチェックしておかないと ASP.NET 5 用のバージョン(6.0.0-beta6)が出てきませんので注意してください。
f:id:aetos382:20150830021144p:plain:w400

Microsoft.Azure.AppService.ApiApps.Service も必要であるような記述も一部にありますが、最小要件としては必須ではありません。
これは、認証や、Logic Apps との連携に使用するライブラリだと思われます。*3
なお、このライブラリには .NET Core 用のパッケージがまだ用意されていませんので、利用する場合は project.json の frameworks から dnxcore50 を削除する必要があります。


gist9d840bc41773340474a8

Swagger の設定

インストールした Swashbuckle は ASP.NET Web APISwagger を使うためのライブラリです。
Swagger は API 定義を JSON 形式で出力するので、それを読み取っていろいろ(ドキュメントを作るとか、クライアントを自動生成するとか)できます。

今回、Swagger の設定は、SwaggerConfig.cs というファイルを作り、そこでまとめて行いました。
SwaggerConfig は Startup.cs から呼び出すようにしています。

最低限、AddSwagger と UseSwagger の 2 つのメソッド呼び出しは必須です。
UseSwaggerUi もやっておくと、Swagger UI が使えるようになります。*4
サンプルでは、それぞれ URL を指定していますが、省略時のデフォルト値と同じにしています。*5

UseSwagger の URL には、{apiVersion} という文字列を含む必要があるようです。
また、この時、AddSwagger のオプションで Version を指定する必要があります。

BasePath の指定が無いと、ローカルでのデバッグ時には動くのですが、Azure にデプロイすると警告が出ます。そのため "/" を指定しています。
Schemes は、Azure API App の場合、http でアクセスしても https にリダイレクトされてしまうようなので*6 https のみにしています。

また、Swagger では各アクションに固有の ID を振る必要がありますが、デフォルトではクラス名とメソッド名から ID が組み立てられるため、Get アクションで ID の重複が起きてしまいます。
そこで、ASP.NET 4 のプロジェクトを参考に、引数名も ID に入れるよう OperationFilter の設定をしています(IncludeParameterNamesOperationFilter.cs)。
ASP.NET 5 版ではメンバー名が変わっていたり、.NET Core では TextInfo が使えなかったりしますので、そのあたりは適宜アレンジを加えています。

apiapp.json の編集

apiapp.json は Azure が利用する APIメタデータ ファイルです。
id と title、それから Swagger を参照するための URL を実際のものに合わせて書き替えています。

この URL については後でもう一度触れます。

デプロイ

あらかじめダウンロードしておいた発行プロファイルを使ってデプロイします。
メニューの【ビルド】から【ApiAppAspNet5 の発行】を選択。*7
【インポート】を選択して発行プロファイルを選択したら、後はデフォルトで構いません。
f:id:aetos382:20150915194130p:plain:w400

デプロイが終わるとブラウザーが立ち上がって 404 エラーが出ますが焦ってはいけません。
API なので表示すべきトップページなど無いのが正常です。

Swagger を見る

URL の末尾に swagger/ui と入力してみましょう。
こんなページが表示されれば成功です。

f:id:aetos382:20150915194553p:plain:w400

Azure ポータルでの確認

Azure ポータルに戻って、API App を開き、【API definition】をクリックしてみましょう。
f:id:aetos382:20150915195252p:plain:w400
…エラーになりましたね。

Failed to get metadata for 'ApiAppAspNet5' from endpoint '/swagger/docs/v1': Failed status code: 'NotFound'. Response Body: ''.

Azure ポータルは API 定義を表示するために Swagger メタデータを必要とします。
本来であれば、apiapp.json 中で apiDefinion に指定した URL を見に行って欲しいのですが、現在はまだ API App がプレビュー版なので、apiapp.json の設定は無視され、常に /swagger/docs/v1 を見に行ってしまいます。

これに対応する方法は 2 つ考えられます。
一つは Swagger の URL を /swagger/docs/v1 に変更する方法。
SwaggerConfig.cs 中の UseSwagger の引数を変えればよいでしょう。apiapp.json の設定値も戻しておきましょう。

今回は別の方法を採ります。
そもそも、これまでやってきたことは何も間違えてはいないのです。ただ Azure が本来すべき仕事をしていないだけ。
そのためにアプリケーションに手を入れるのは避けたいので、Swagger の URL は変えずに何とかしましょう。

URL Rewrite を使おう

要は、/swagger/docs/v1 を見に来る Azure を、/swagger/v1/swagger.json にリダイレクトしてしまえばよいわけです。
というわけで URL Rewrite を使いましょう。

IIS マネージャーで Azure に接続してもいいですが、今回は Azure Web App の管理ツールである Kudu を使ってお手軽に web.config を書き替えます。
Kudu を開くには、API App の URL の azurewebsites.net の前に scm. というのを付けます。
そうしたら【Debug Console】から【PowerShell】を選択します。
【site】→【wwwroot】と階層を下って、【web.config】の左にある鉛筆マークをクリック。
どのように書き替えればよいかは GitHub の方を参照してください。

f:id:aetos382:20150915210416p:plain:w400
f:id:aetos382:20150915210420p:plain:w400

Azure ポータルに戻って、再度 API definition の表示を試みます。
こうなれば大成功。

f:id:aetos382:20150915201303p:plain:w400

プロジェクト内の web.config も書き替えておくべきですが、いずれ Azure ポータルがちゃんと apiapp.json を見るようになったら、また Kudu で書き替えれば、再デプロイする必要はありません。*8
Kudu 便利ですねー。
なお、Windows 10 の Edge だとプルダウン メニューが動きませんでした。Windows 10 をお使いの方は IE 等、他のブラウザで操作してください。MS 仕事しろ。

まとめ

MS 仕事しろ。

*1:ASP.NET Web API そのものについては割愛します。

*2:実働環境では要件に応じて適切に設定してください。

*3:違ってたらマサカリ優しくご指摘ください。

*4:JSON 形式の API 定義をグラフィカルに表示してくれたり、その場で実行したりすることができます。

*5:書いておかないとわからなくなっちゃうんです…

*6:何か設定する方法はあるのだと思いますが、今回は調べていません。

*7:ソリューション エクスプローラーだと「公開」になってるんですよね…

*8:このあたりは各々ポリシーがあると思いますが…