Привет, ребята! Можно ли использовать application/json в качестве Content-Type при обновлении поста через API? В документации сказано, что можно, но я начинаю думать, что это невозможно…
Я постоянно получаю ошибку ["BAD CSRF"] и понятия не имею, что это значит.
Если мне нужно использовать multipart/form-data, не могли бы вы подсказать, как сформировать PUT-запрос? Особенно в части data.
header = CaseInsensitiveDict()
header["Authorization"] = '{"api-key": "longapikey", "api-username": "myusername"}'
{
"post": {
"raw": "Крутой пост, но вот обновлённый текст тела поста",
"edit_reason": "Я изменил это, потому что могу."
}
}
resp = requests.put(url, headers=headers, data=data)
Это очень странно: когда я форматирую заголовок так, как вы показали, я получаю:
{"errors":["У вас нет разрешения на просмотр запрошенного ресурса. Имя пользователя или ключ API недействительны."],"error_type":"invalid_access"}
Если я добавляю ["Authorization"], всё работает.
Это для простого GET-запроса, но я всё ещё не могу выполнить PUT.
Я ожидал, что заголовок, который работает для одной операции, будет работать для всех (при условии, что ключ является global — а он им и является). Поэтому я пока не слишком беспокоюсь о заголовке — или мне стоит?
Скорее всего, ваш запрос PUT сформирован некорректно, особенно если GET работает, даже когда вы используете заголовок Authorization неправильно. Он работает, потому что для выполнения запроса GET авторизация вообще не требуется. Мы даже не проверяем заголовок Authorization, если вы его передаёте. Запросы GET к публичным конечным точкам будут работать корректно без каких-либо заголовков.
@pedroleaoc вот небольшой пример скрипта на Python, демонстрирующий, как выполнять аутентифицированные запросы и отправлять данные для PUT/POST-запросов.
# discourse-api-demo.py
import requests
from requests.structures import CaseInsensitiveDict
# Базовый GET-запрос к общедоступному URL, заголовки не требуются.
url = "http://localhost:3000/posts/10.json"
resp = requests.get(url)
print(resp.status_code)
print(resp.content)
# GET-запрос к приватной конечной точке. Требуются заголовки аутентификации.
url = "http://localhost:3000/admin/users/list/active.json"
headers = {'Api-Username': 'system', 'Api-Key': '5c1c57915e2...'}
resp = requests.get(url, headers=headers)
print(resp.status_code)
print(resp.content)
# PUT-запрос с телом запроса
url = "http://localhost:3000/posts/10.json"
data = { 'raw': "Крутой пост, но вот обновлённое содержимое поста", 'edit_reason': "Я изменил это, потому что могу." }
resp = requests.put(url, headers=headers, json=data)
print(resp.status_code)
print(resp.content)
Вместо того чтобы кодировать dict самостоятельно, вы также можете передать его напрямую, используя параметр json (добавлен в версии 2.4.2), и он будет закодирован автоматически:
Пожалуйста, посмотрите мой пост, расположенный сразу над этим. Вы можете использовать формат json=data вместо data=data в вашем запросе, и библиотека python requests сама установит заголовок content-type как application/json, что и следует использовать.
Отлично, теперь я могу редактировать свой пост! Спасибо за помощь!
Есть ещё одна проблема: используемый мной ключ — global. Когда я пытаюсь использовать ключ только с правами write и read, получаю ошибку: Вы не имеете права просматривать запрошенный ресурс. Имя пользователя API или ключ недействительны. При редактировании поста через графический интерфейс, помимо запроса, который фактически редактирует пост (posts/post_id.json), отправляется ещё один PUT-запрос к URL темы, который я не могу воспроизвести с ограниченным ключом API — только с глобальным. Однако я не понимаю, почему без этого дополнительного PUT-запроса, который происходит в GUI, я не мог бы редактировать пост через API.
РЕДАКТИРОВАНИЕ: Технически мой ключ API охватывает путь /t/:slug/:topic_id, куда указывает этот PUT-запрос.