概要
前回、Google App EngineでFlaskを利用する際のNo Content(204)ステータスコードの利用方法を調査したけれど、原因がいまいち特定できていませんでした。
原因がわかりました!
Google App Engine上のFlaskでレスポンスをNo Content(204)で返す方法を調べた
https://cloudpack.media/44234
原因
No Content(204)を返すとき、Content-Length
が0
じゃないと、GAEでgunicornプロセスが落ちる(白目
検証
前回の記事のソースを利用して検証します。
GitHubにもソースをアップしていますので、ご参考ください。
https://github.com/kai-kou/how-to-use-gae-no-content
環境設定
> git clone https://github.com/kai-kou/how-to-use-gae-no-content.git > cd how-to-use-gae-no-content > python -m venv venv > . venv/bin/activate > pip install -r requirements.txt
環境が用意できたらflaskを起動します。
> flask run
curl
で確認してみます。
flask_runしていないコンソール
> curl 127.0.0.1:5000/good_no_content -i HTTP/1.0 204 NO CONTENT Content-Type: application/json Content-Length: 0 Server: Werkzeug/0.14.1 Python/3.6.6 > curl 127.0.0.1:5000/bad_no_content -i HTTP/1.0 204 NO CONTENT Content-Type: application/json Content-Length: 5 Server: Werkzeug/0.14.1 Python/3.6.6
はい。GAEにもデプロイしています。
GitHubからソース取得している場合、app.yaml
のservice
を変更するか削除してください。
> touch app.yaml > gcloud app deploy
デプロイできたら確認してみます。
> curl https://[GAEのサービス名]-dot-[GCPのプロジェクトID].appspot.com/good_no_content -i HTTP/2 204 content-type: application/json x-cloud-trace-context: 436098c2c9196bd34b10448cb57643b0;o=1 server: Google Frontend alt-svc: quic=":443"; ma=2592000; v="44,43,39,35" > curl https://[GAEのサービス名]-dot-[GCPのプロジェクトID].appspot.com/bad_no_content -i HTTP/2 500 x-cloud-trace-context: 0f98972aa581c03afa7af2d39596b8af;o=1 content-type: text/html; charset=UTF-8 server: Google Frontend content-length: 323 alt-svc: quic=":443"; ma=2592000; v="44,43,39,35" (略)
はい。bad_no_content
はやはりエラーになります。
前回はHTTPステータスコードとContent-Type
しかみてなかったのですが、ローカル実行時のContent-Length
に違いがあることのがわかりました。
# Good Content-Length: 0 # Bad Content-Length: 5
そのへんから情報を漁っていると、Flask-RESTfulというライブラリのGitHubにそれらしきIssueがありました。。。
204 status returns non-zero content-length
https://github.com/flask-restful/flask-restful/issues/736
試しに、だめな方の実装を変更してみます。headers
にContent-Length
を追加します。
app.py(一部)
# GAEでエラーになる @app.route('/bad_no_content', methods=['GET']) def bad_no_content(): response = make_response(jsonify(None), 204) response.headers['Content-Length'] = 0 return response
> flask run
flask_runしていないコンソール
> curl 127.0.0.1:5000/bad_no_content -i HTTP/1.0 204 NO CONTENT Content-Type: application/json Content-Length: 0 Server: Werkzeug/0.14.1 Python/3.6.6
GAEにデプロイして確認します。
> gcloud app deploy > curl https://[GAEのサービス名]-dot-[GCPのプロジェクトID].appspot.com/bad_no_content -i HTTP/2 204 content-type: application/json x-cloud-trace-context: b26c9b04d914cbc0e38de16f8d7fe28f;o=1 server: Google Frontend alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
おぅ。。。エラーが発生しなくなりましたぁぁぁ。
嫌がらせに以下のように変更して実行してみます。コンテンツを明示的に返すようにします。
app.py(一部)
# GAEでエラーになる @app.route('/bad_no_content', methods=['GET']) def bad_no_content(): # コンテンツを設定してみる response = make_response(jsonify({'message':'hoge'}), 204) response..headers['Content-Length'] = 0 return response
> flask run
flask_runしていないコンソール
> curl 127.0.0.1:5000/bad_no_content -i HTTP/1.0 204 NO CONTENT Content-Type: application/json Content-Length: 0 Server: Werkzeug/0.14.1 Python/3.6.6
GAEにデプロイして確認します。
> gcloud app deploy > curl https://[GAEのサービス名]-dot-[GCPのプロジェクトID].appspot.com/bad_no_content -i HTTP/2 204 content-type: application/json x-cloud-trace-context: 27edb7f43586aec504497cc61b46be4d date: Tue, 30 Oct 2018 08:48:21 GMT server: Google Frontend alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
ヘッダー優先みたいですね。
ついでに''
とNone
とでレスポンス内容の際をみてみました。jsonify
を利用すると改行が入ってしまう模様。イラナイヨォNone
はnull
に変換されます。
レスポンス実装 | レスポンス内容 |
---|---|
make_response(”, 204).data | b” |
make_response(None, 204).data | エラー |
make_response(jsonify(”), 204).data | b'””\n’ |
make_response(jsonify(None), 204).data | b’null\n’ |
make_responseが面倒だ!
make_response
を利用せず、以下のように実装することもできます。
headers = { 'Content-Type': app.config['JSONIFY_MIMETYPE'], 'Content-Length': 0 } return '', 204, headers
まとめ
No Content(204)を返すとき、Content-Length
が0
じゃないと、GAEでgunicornプロセスが落ちるから気をつけましょう(再掲
前回は原因不明のまま、回避策だけを見出して終わったのですが、今回、なんとか原因がわかって、モヤモヤが晴れました^^
こういった問題が起こり得るので、Dockerイメージでほぼ同一環境!最強!ヒャッハーと油断しているといざクラウド環境に上げてから、痛い目に合いそうで(実際合った)怖いですね。
早めに運用環境で検証しておくのに越したことはないですね。教訓!
参考
204 status returns non-zero content-length
https://github.com/flask-restful/flask-restful/issues/736
元記事はこちら
「Google App Engine上でFlaskを利用してNo Content(204)を返すとき、'''Content-Length''' が0じゃないとエラーになる」