/var/www/yatta47.log

/var/www/yatta47.log

やったのログ置場です。スクラップみたいな短編が多いかと。

はてなブログAtomPubでカテゴリだけPUTすると400エラーになる原因と対処法

はてなブログのAtomPub APIで既存記事のカテゴリだけ変えようとしてPUTしたら、400 Bad Requestが返ってきました。 原因はAtomPubのPUTが「部分更新」ではなく「エントリ全体の置換」だから。content を含めた全フィールドを送る必要があります。

何が起きたか

投稿済みの記事にカテゴリを追加したくて、カテゴリだけ入れたXMLをPUTで送ったんですよ。

# カテゴリだけのXMLを用意して送った
curl -s -X PUT \
  -H "Content-Type: application/atom+xml;type=entry" \
  -u "${HATENA_USER}:${HATENA_API_KEY}" \
  --data-binary '<entry xmlns="http://www.w3.org/2005/Atom">
    <category term="新カテゴリ" />
  </entry>' \
  "https://blog.hatena.ne.jp/${HATENA_USER}/${HATENA_BLOG_DOMAIN}/atom/entry/${ENTRY_ID}"

返ってきたのがこれ。

400 Bad Request

XMLの書式ミスかと思って何度か書き直したんですけど、そこじゃなかった。

原因

AtomPub(RFC 5023)のPUTは「リソースの部分更新」ではなく「リソース全体の置換」です。

HTTPの仕様としても、PUTは対象リソースの現在の表現をリクエストの内容で完全に置き換える操作。部分更新はPATCH(RFC 5789)の役割なんですが、はてなブログのAtomPubはPATCHをサポートしていません。

つまりこういうことです。

やったこと サーバの解釈
カテゴリだけ送った 「タイトルも本文もないエントリに置き換えろ」
→ 不完全なエントリ → 400 Bad Request

UIだとカテゴリだけ編集できるので「APIでもできるでしょ」と思いがちですが、APIの意味論はUIの粒度と一致しないんですよね。

対策

GETしてから全体をPUTする(確実)

正しい手順は「現在のエントリを丸ごと取得 → カテゴリだけ編集 → 全体をPUT」です。

# 1) 現在のエントリを取得
curl -s -u "${HATENA_USER}:${HATENA_API_KEY}" \
  "https://blog.hatena.ne.jp/${HATENA_USER}/${HATENA_BLOG_DOMAIN}/atom/entry/${ENTRY_ID}" \
  -o /tmp/entry.xml

取得したXMLの <category> 要素を編集します。

# 2) カテゴリを追加(既存のcategoryの後に追加する例)
# /tmp/entry.xml を編集して <category term="新カテゴリ" /> を追加

# 3) 編集後のエントリ全体をPUT
curl -s -X PUT \
  -H "Content-Type: application/atom+xml;type=entry" \
  -u "${HATENA_USER}:${HATENA_API_KEY}" \
  --data-binary @/tmp/entry.xml \
  "https://blog.hatena.ne.jp/${HATENA_USER}/${HATENA_BLOG_DOMAIN}/atom/entry/${ENTRY_ID}"

ポイントは --data-binary を使うこと。-d だと改行がストリップされてMarkdown本文が壊れます。

Content-Typeは application/atom+xml;type=entry を指定しています。application/atom+xml だけでも動きますが、明示した方が安全です。

PUT前の検証

送信前に content が残っているか確認しておくと事故を防げます。

if ! grep -q '<content' /tmp/entry.xml; then
  echo "content要素がありません。PUTすると記事本文が消えます"
  exit 1
fi

件数が少ないならWebUIで(速い)

カテゴリ変更が数件だけなら、正直WebUIから手動でやった方が早いです。GET→編集→PUTの手順を組むコストと見合わないケースもあるので、自動化する前に件数を確認するのがおすすめです。

まとめ

はてなブログAtomPubのPUTはエントリ全体の置換なので、カテゴリだけ送ると400になります。 「GET→編集→PUT」を標準手順にしておけば安全です。この「PUTは置換、部分更新はPATCH」というHTTPの意味論は他のAPIでも同じなので、覚えておくと同種のハマりを減らせます。

参考