はてなブログに記事を投稿するプログラムを書いていたら、ハマったことがあったのでメモしておきます。
誰かのためになれば
はてなブログAtomPub
はてなブログやはてなブックマークをプログラムで操作できるAPIがあります。はてなブログAtomPub用のものは、ここにあります。
https://developer.hatena.ne.jp/ja/documents/blog/apis/atom
ドキュメントがキレイになってる。はてなだけはAPIを提供し続ける気持ちがありそうだ。
使った環境
今回、プログラムを作った環境です
- Laravel Framework 10.48.4
- HTTPファサード
エラーになったプログラム
LaravelのHTTPファサードを使ってxml文字列をポストするだけです。はてなのAPIはベーシック認証が必要です。はてなブログの設定画面から確認できます。
//XMLはシンプルにvalidになるものを $xml = '<entry></entry>'; $response = Http::withBasicAuth($username, $api_key)->post($endpoint, $xml); var_dump($response->status()); var_dump($response->body());
エラーの内容
400 XML Parse Failed
最初は、プログラムで作ったXML文字列を投げ込んでParse Failedだったので、XMLが間違えてる可能性もあり、上のプログラムのように極限までシンプルなXMLにしたけど、同じくParse Failedのエラー。エラーになる要素、ないだろ!?
しかも、プログラムから作ったXML文字列をVS CodeのHttp Clientで投げ込むと成功する。XMLに間違いはなさそうだ。
全く分からなかった。
解決のヒント
http通信をデバッグにしてみたら、content-length
が18バイトだった。送っているXML(<entry></entry>
)は15バイトである。おかしいな、3バイト多いぞ。
3バイト多いって言ったら、BOMかもな?
作った文字列と送信したデータの違いを見る方法を探すことができなかったので、BOMの疑いがある、というところまで。
解決した方法
Post
メソッドにxmlを入れるのではなく、HttpファサードのwithBody
でxmlを指定してみたら、上手くいった。
//XMLはシンプルにvalidになるものを $xml = '<entry></entry>'; $response = Http::withBasicAuth($username, $api_key)->withBody($xml)->post($endpoint); var_dump($response->status()); var_dump($response->body());
エラーメッセージ
400 Entry Title required
「エントリーにタイトルが必須です」エラーに変わったので、XML自体は読み込んでもらえたっぽい。
Content-Length
も15バイトになったし、やっぱりBOMだったんだろうな。postメソッドで文字列にBOMを付けない方法があればいいのだけど、解決したからよしとする
参考にした資料
BOMに疑いありなことを見つけたサイト
PUT/POSTリクエストで400 XML Parse Failedが返される不具合に対する対処として、はてなブログAtomAPIに送信するXML文書を常にBOMなしのUTF-8とするように修正
https://smdn.jp/works/tools/HatenaBlogTools/#changes_v3.1
withBodyで書けばよいことを見つけた記事
https://stackoverflow.com/questions/60318425/posting-xml-using-guzzle-client-laravel