2013年11月19日

追記 2014/11/20 14:00:00

わりと JSON やら XML やら各種フォーマットで API を運用している環境がある場合に JSON API の時だけ X-JSON-Status にすると XML とかの時と整合性取れないし、 X-XML-Status みたいのを量産するのは困る的なレビューを頂いたので X-JSON-Status をやめて X-API-Status にしました。

へたに JSON に限定するから REST とか JSON-RPC とかいわれるんや! X-API-Status にしたら全部解決したし MessagePack な API でも使い回せるって songmu さん言ってた! XML とかからどうやって引っこ抜くかまで僕考えられないけど!

ちなみに Amon2 の方も X-API-Status にしたのを merge 済み

始めに

ここ最近の Web 制作の現場ではスマホアプリが主流になってきており、 server side の開発も従来の text/html が主要な content type では無くなり application/json を主に返す server side application を実装する人も増えてきたのではないでしょうか。
いわゆる JSON API ですが、現状これらの仕様は各者それぞれが自由に(ある程度メジャーなサービスの模倣はあるにせよ)行っています。それが良い事か悪い事か僕にはわかりませんが、今回は皆が捗る一つの提案をしたいと思います。

現状の JSON API

一般的な JSON API を作る事を考えた時に、その API が error を発生させる時と言うのは API を使う client の request 内容に不備がある時や、関連する他の API が error を返す時はたまた IO 等がエラーを返した時になると思います。

基本的にはそれらの error 全ては JSON API を開発する人がハンドリング出来る内容なので、適切な JSON を response body として返してる事でしょう。当然 API を利用する client は server side が返す物は全て JSON であるという仮定で実装されてしまってるのかも知れないので(そういう開発者は良く無いけど)、意図しない例外も全て捕まえて JSON にして返す実装もあるでしょう。

HTTP status code

ここまでは割と普通の内容ですね。正常にリクエストが完了する事もあれば error を返す物があります。

これから本題なのですが、もしも認証が必要な JSON API があるとして client からの request が認証を満たさなかった場合の HTTP status code はどうするのが適切でしょうか?
普通に考えると

401 Unauthorized
等を返して {"errorMessage":"unauthorized"} みたいに返してしまうのかもしれません。
そしてちょっと前提が変わりますが、あなたが作っている JSON API が更に他の API を使っていたとします。その API への request が timeout してしまった場合も、同じ方式で行くと
504 Gateway Timeout
を返してしまうのではないでしょうか?

わりとちゃんとしてるサービスの運用をしている人達は全ての HTTP status code を監視して 50x が多く出たら何かの障害がある? 40x が出たらリンク先が切れてておかしいんじゃね?みたいな格闘を日々しています。もし、あなたが書いた JSON API のエラーコードを HTTP status code で表現した場合に運用者にとってのノイズになるかもしれません。ようするにアプリケーションが正しくハンドリングしてレスポンスを返している場合は正常系としておいて本当に問題があった時だけ異常系の status code にする!とかですね。若しくは深遠な理由で通常の HTML application や static contents と同じ host で JSON API をサービスするとかそういう状況も泥臭い世の中には存在するので、そういう時はノイズになりやすいですね。

また、通常 HTTP status code では表現不能な error response を仕様に組み込まれる事も多々あります。そういった場合は結局 JSON の中で {"status":415,"errorMessage":"foo"} とか {"errorCode":415,"errorMessages":["foo","bar"]} などのような表現をするのでは無いでしょうか。

RESUful な人達には怒られるけど、そもそも HTML based な Web Application においては、 client からの入力データの不備は

200 OK
で返した上でエラーの状態を HTML の中に記載して返してるではありませんか。
そういう発想のもと上記の問題を解決する為に「基本的には 200 OK を返して、それとは別に JSON の中で status code を与えて client に処理してもらう」みたいな実装も一つの手段として使われます.

JSON status code の問題

一つ目の問題点としては、ここ最近僕も JSON API を沢山書くようになってきて感じてる事として一般的な web server が書き出す access log に JSON の中の status code が入らない、入れる為には apache の中で JSON をパースするか、 response body 全部をログにつっこむしかない!そんなの現実じゃない!
なぜ欲しいかというと、どの API に大してどんな error を返してるのかを tail log とかして問題解決したい時があったりするんです。現状だと別にログ履くとかしないし一応ログは吐いてるけど通常の access log 以上の情報量のログを常に吐ける程ぼくは人間が出来ていない。

二つ目としては、 response で送られた JSON をパースする必要がないレベルの error があった時に、 client 側に無駄な負担をかけさせちゃう。
これはだいぶ後付けの理由である。

X-API-Status header

そこで導きだされた解決策として JSON の中野 status code を X-API-Status というヘッダで出力するという事。

response header として出力すれば apache の log とかでも

%{X-API-Status}o
とかで出力できるし、 client 側の実装でも header を見るだけで処理の分岐が可能になる。

そして access log に application layer status code が吐き出されるという事は、モリス作の紀香とかを使って直近1分以内に以上系のレスポンスが増えたのでアラートを出すみたいな運用基盤が新たに構築出来る!すべての人類が幸せになるじゃないか!

という事で、皆さんに嬉しいお知らせとして Amon2 で X-API-Status を簡単に出力出来る patch が先ほど merge されたので、これがリリースされればお気軽にこの運用が可能になります。
以下のような簡単な感じで使えます。

    __PACKAGE__->load_plugins(
        'Web::JSON' => { status_code_field => 'status' }
    );
    ...
    $c->render_json({ status => 200, message => 'ok' })
    # send response header 'X-API-Status: 200'

503 などの致命的なエラーはどうする?

それでもやっぱり致命的なエラーは HTTP status code 側で表現されて然るべき、ただし response body は JSON を必ず返す。という感じになってしまうので、 server side で美味しい事だらけなんですが、 client side の実装が負担増える気がしています。
ただ今までも HTTP status code をみてから json.status やら json.error やら、正常系だとそもそも status code がないよ!みたいなカオスだったんで、それが HTTP status code と X-API-Status をみるだけで respnse の状態がわかる感じになっていくから、まだマシになるのかなーとおもいます。

まとめ

  • 近年の JSON API での status code の実装についてまとめました
  • それらにまつわる面倒ごとを改善する X-API-Status header の提案をしました

追記

  • Access-Control-Expose-Headers: X-API-Status は、アプリケーション開発者が状況に応じて返してあげればすむ感じっぽいから、現状大丈夫そうだった


NORIKA―藤原紀香写真集
大沢 尚芳
ワニブックス
売り上げランキング: 34,355
Posted by Yappo at 2013年11月19日 15:04 | TrackBack | tech
Comments
Post a comment









Remember personal info?






コメントを投稿する前に↓の場所にnospamと入力してください。