0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Confluence REST API: リクエストヘッダが`Content-Type: application/json`でリクエストボディが`application/x-www-form-urlencode`形式だと500エラーが発生した

Last updated at Posted at 2025-12-21

環境

  • 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エラーはサーバ障害とは限らない。改めてリクエストを確認した方がよい
  1. https://requests.readthedocs.io/en/latest/user/quickstart/#more-complicated-post-requests

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?