はじめに
とにかくかんたんなWeb APIアプリケーションを書くのに、pythonは非常にお手軽。
このお手軽さに乗っかっていて、しかもpath routingも簡単にできるフレームワークがFlask。Azure App ServiceでAPI Appsをpythonで書く場合のサンプルもFlaskで用意されている。
自分は仕事としてはjavaのほうがメインなんだけど、ほとんどの言語を制約されないプログラムはpythonで書いていて、同じノリでAPI AppsにFlask使ったAPIをデプロイしたら、これがうまく動かない。
問題点と対策
事象
何が動かなかったかというと、Flaskの@app.route()が動かなかった。
正確には、@app.route("/")は動くけど、@app.route("/user/<id>/status")みたいなやつが動かない。GET /user/1/status HTTP/1.1がIIS(?)の404で帰ってくる。
これは、何がまずいかというと、(多分だけど)Web AppsはIISがフロントに上がっていてpythonのFlaskにディスパッチしているところが、デフォルトでは"/"だけがFlaskに渡されていて、/user/1/statusみたいなパスは対象外になっているためだと思う1。
方針
これがjavaなら、url-patternでどのServletに処理させるかをWeb Application Containerに登録して実現しているところで、cgiならHTTP ServerがRequest Handlerでマッピングを取ってるところ。なので、Web Appsに動き方を教えてあげてるところだろう、という想像がつく。
Azure App Service Web Apps による Python の構成によると、
サーバーによる要求の処理方法を指定するには、web.config ファイルを作成する必要があります。
リポジトリに web.x.y.config ファイルがある場合は (ここでは x.y は選択した Python ランタイム)、Azure が適切なファイルを web.config として自動的にコピーします。
とある。今回はpython3.4で実行しようとしてたので、web.3.4.configになにかしてやればよさそう。
解決策
まず、これを読んで実装してセキュリティがずるずるになっても責任は取れないから、問題ない内容かちゃんと各自で判断してくださいね(棒読み)
で、やり方なんですが、先ほどの記事にある通り、
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="WSGI_ALT_VIRTUALENV_HANDLER" value="app.wsgi_app" />
<add key="WSGI_ALT_VIRTUALENV_ACTIVATE_THIS"
value="D:\home\site\wwwroot\env\Scripts\python.exe" />
<add key="WSGI_HANDLER"
value="ptvs_virtualenv_proxy.get_venv_handler()" />
<add key="PYTHONPATH" value="D:\home\site\wwwroot" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<handlers>
<remove name="Python27_via_FastCGI" />
<remove name="Python34_via_FastCGI" />
<add name="Python FastCGI"
path="handler.fcgi"
verb="*"
modules="FastCgiModule"
scriptProcessor="D:\Python34\python.exe|D:\Python34\Scripts\wfastcgi.py"
resourceType="Unspecified"
requireAccess="Script" />
</handlers>
<rewrite>
<rules>
<rule name="Static Files" stopProcessing="true">
<conditions>
<add input="true" pattern="false" />
</conditions>
</rule>
<rule name="Configure Python" stopProcessing="true">
<match url="(.*)" ignoreCase="false" />
<conditions>
<add input="{REQUEST_URI}" pattern="^/static/.*" ignoreCase="true" negate="true" />
</conditions>
<action type="Rewrite" url="handler.fcgi/{R:1}" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
になっていれば、ちゃんと@app.route("/user/<id>/status")みたいなroutingも機能するもよう2。
<system.webServer>ほげほげほげ</system.webServer>の中の<rule name="Configure Python" stopProcessing="true">~</rule>ほうの定義がURLパターンを引っ掛けてpythonに渡そうとしているっぽいけど、動作は改めて調べようかと。
多分、IISのドキュメント読めば動作が分かるんだとおもうのだけれど。