環境
- Confluence 6.15.7
- Python 3.13
- requests 2.32.3
やりたいこと
Confluenceのコンテンツを更新するAPIを使って、ページを更新したいです。
以下のPythonスクリプトを実行することで、ページを更新できました。
sample.py
import requests
import os
base_url = "https://example.com/confluence"
username = os.getenv("CONFLUENCE_USER_NAME")
password = os.getenv("CONFLUENCE_USER_PASSWORD")
content_id = "xxxxxxx"
new_page_body = "<p>New page data.</p>"
# 既存のコンテンツ情報を取得
response = requests.get(
f"{base_url}/rest/api/content/{content_id}",
auth=(username, password)
)
response.raise_for_status()
old_content = response.json()
# リクエストボディを構築
request_body = {
"version": {
"number": old_content["version"]["number"] + 1,
},
"title": old_content["title"],
"type": old_content["type"],
"body": {
"storage": {
"value": new_page_body,
"representation": "storage"
}
},
}
# コンテンツを更新
response = requests.put(
f"{base_url}/rest/api/content/{content_id}",
json=request_body,
auth=(username, password)
)
response.raise_for_status()
ハマったこと
上記の正しく動くコードに到達するまでに、結構ハマってしまいました。その内容を記載します。
私は以下のようなコードにしてしまいました。
- リクエストボディを
requests.putメソッドのjson引数でなくdata引数に渡した-
data引数にdictを渡すとapplication/x-www-form-urlencoded形式になる 1
-
-
header引数でContent-Type: application/jsonを明示的に指定した
response = requests.put(
f"{base_url}/rest/api/content/{content_id}",
data=request_body,
headers={"Content-Type": "application/json"},
auth=(username, password)
)
このコードを実行すると、500エラーが発生しました。
requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://kurusugawa.jp/confluence/rest/api/content/xxxxxxx
レスポンスには有益な情報は含まれていませんでした。
{"statusCode":500,"message":"","reason":"Internal Server Error"}
なお、header引数にContent-Typeを明示的に指定しなければ、requestsモジュールが自動的にapplication/x-www-form-urlencodedを設定します。その場合は415エラーが発生しました。
response = requests.put(
f"{base_url}/rest/api/content/{content_id}",
data=request_body,
auth=(username, password),
)
requests.exceptions.HTTPError: 415 Client Error: Unsupported Media Type for url: https://kurusugawa.jp/confluence/rest/api/content/xxxxxxx
Content-Typeを明示的に指定していなければ、415エラーでコードの問題にすぐ気づけたと思います。しかし、500エラーだったため、自分の書いたコードのどこに問題があるか分からず、エラーの原因調査に時間がかかってしまいました。
今回は、http.client.HTTPConnection.debuglevel=1を設定してリクエスト情報を出力することで、エラーの原因に気づくことができました。
教訓
-
requestsモジュールでjson引数を使う場合は、Content-Typeを自分で指定しない方がよい。リクエストボディとリクエストヘッダの不整合を防げる -
requestsモジュールのdata引数とjson引数の違いをちゃんと理解する - ハマったら、とりあえず
http.client.HTTPConnection.debuglevel=1でリクエスト情報を出力する - 500エラーはサーバ障害とは限らない。改めてリクエストを確認した方がよい