『7回目の出直し🌻』

好きなことを自分のペースで、のんびり更新

はてなのAPIにLaravel のHTTP postでxmlを送信すると「400 XML Parse Failed」エラーになる

はてなブログに記事を投稿するプログラムを書いていたら、ハマったことがあったのでメモしておきます。

誰かのためになれば

はてなブログ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