APIのもやもや感について #40

公開日: 2012-06-23


普段、C++で地味なコードを書いていることが多く、ウェブの技術を使って何かをやるみたいなコードをあまり書かない。

で、たまにそれっぽいことをやってみようとすると、どはまりする。たまにしかやらないから毎回、同じようなところでつまづきがちだ。今回はまったのはウェブのAPIを呼び出すプログラムで、基本的には APIを叩くだけのプログラムなんだけど、えらいはまってしまった。

やりたいことは簡単で Google Documents List API version 3.0 [1] というものを使って、ファイルをアップロードしたり更新したりするということ。Python などで提供されているクライアントライブラリを使えば簡単なんだけども、C++用の手頃なものは見当たらない。

ドキュメントによれば、ファイルのアップロードは以下のように行う。

POST [resumable-create-media link]
Content-Length: 359
Content-Type: application/atom+xml
X-Upload-Content-Type: application/pdf
X-Upload-Content-Length: 1073741824

<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
  <title>Legal Contract</title>
</entry>

HTTP/1.1 200 OK
Location: [next location]
...
PUT [next location]
Content-Length: 524288
Content-Type: application/pdf
Content-Range: bytes 0-524287/1073741824

[bytes 0-524287]
HTTP/1.1 308 Resume Incomplete
Range: 0-524287
Location: [next location]
...

ファイルのアップロードなんかPOSTリクエストで一発、と思いきや、そうではない。最初の POST リクエストでは XMLでメタデータを送るだけ。そうすると、サーバからアップロード用のURLが返ってくる。そのURLに向けて 512KB 分のデータを送ってやると、次のURLを教えてくれる。そういうプロトコルらしい。なんだか複雑だけど、アップロードの中断・復帰をやりやすくするためにこういう仕組みになっているのだろう。

幸いこの部分のコードはすでに他の人が書いていた。が、上のプロトコルは新規のファイルをアップロードするためのもので、既存のファイルを更新する場合は最初のリクエストが少しだけ違う。この部分はいじる必要がある。

具体的には、 以下のような変更が必要であった。

- POST を PUT にする
- ボディを空にして Content-Type は text/plain を指定する
- If-Match: * をつける

たったこれだけの変更なんだけど、このうちひとつでもミスっているとサーバエラーになってしまう。で、これらすべてにつまづいて、無闇に試行錯誤しているうちに、かなりの時間を食ってしまった。最終的にはドキュメントを読み直して、この3つの変更が必要という結論に達したのだが、解決してもあまり達成感を感じないデバッグであった。

ウェブのAPIだろうが、Cの関数呼び出しだろうが、APIを叩くという点では本質的には同じはずなんだけど、どうも前者のほうが、ストレス度合いが高い。これは一体なんででしょうね、ということを同僚に話したところ、「APIというのはブラックボックス度が高いほど、もやもや感が高まるものだ」との答えが返ってきた。これは確かにその通りだと思う。

これに加えて、呼び出すときのパラメータが多いのも、もやもや感を高める。Cの関数でも、引数がひとつの単純なものは空気を吸うように使えるけど、fread(ptr, size, nmemb, stream) みたいな複雑なやつは、慎重にコードを書かないと使えない。4つもパラメータを覚えられないから、マニュアルを毎回チェックしてしまう。少しもやもやする。

そう考えると、上のウェブAPIはすごい複雑だ。なにしろ、パラメータがたくさんあるだけではなく、パラメータを指定する方法もたくさんあるのだ。

- POSTのURLの中で指定する (?convert=false とか)
- HTTPリクエストヘッダの中で指定する (X-Upload-Content-Typeとか)
- ボディのXMLの中で指定する (<title>Legal Contract</title> とか)

やり方は違うが、どの方法も基本的には任意の key value pairs を渡しているだけだ。これは関数呼び出しで表現するなら、こんな感じになるだろう。

  Request(table1, table2, table3)

3つのパラメータはそれぞれ文字列→文字列のハッシュテーブルで、任意のパラメータを渡せる。ぜんぶ文字列だから、型の検査もなく、タイポしたら終わりだ。さらに悪いことに、この関数はすぐには結果をくれない。すごく時間がかかるときもある。エラーになるときもあるが、中身はブラックボックスで何が起きているのかわからない。これはもやもや感が高い!

というわけで、ウェブのAPIのもやもや感が高い理由はわかったんだけど、わかったからといって、もやもや感が減るわけではないのであった。

[1] https://developers.google.com/google-apps/documents-list/

Satoru Takabayashi