Qual é o `content-type` correto ao editar posts?

Ei pessoal, posso usar application/json como Content-Type ao atualizar um post via API? A documentação diz que sim, mas estou começando a achar que não posso..

Estou recebendo constantemente [\"BAD CSRF\"] e não faço ideia do que isso significa.


Se eu precisar usar multipart/form-data, vocês têm alguma dica sobre como construir minha requisição PUT? Especialmente a parte do data.

header = CaseInsensitiveDict()
header["Authorization"] = '{"api-key": "longapikey", "api-username": "myusername"}'
{
  "post": {
      "raw": "Post legal, mas aqui está uma atualização para o corpo do post",
      "edit_reason": "Mudei isso porque posso."
   }
}

resp = requests.put(url, headers=headers, data=data)

Obrigado!

Você precisa enviar a Api-Key e a Api-Username como cabeçalhos separados, e não dentro do cabeçalho Authorization. Algo assim deve funcionar melhor:

header = CaseInsensitiveDict()
header["Api-Key"] = 'longapikey'
header["Api-Username"] = 'myusername'

Isso parece ser o mesmo problema do seu tópico anterior:

Obrigado pela sua resposta, David.

Isso é super estranho. Quando eu formato o cabeçalho da maneira que você mostrou, recebo:

{"errors":["Você não tem permissão para visualizar o recurso solicitado. O nome de usuário ou a chave da API é inválido."],"error_type":"invalid_access"}

Se eu adicionar o ["Authorization"], funciona.

Isso é para uma simples requisição GET, mas eu ainda não consigo fazer o PUT.

Eu esperaria que um cabeçalho que funciona para uma operação funcionasse para todas (desde que a chave seja global - o que é o caso). Então, não estou muito preocupado com o cabeçalho no momento - ou deveria estar?

Obrigado!

Provavelmente há algo malformado na sua requisição PUT, já que o GET está funcionando mesmo quando você está fazendo do jeito errado com o cabeçalho Authorization. Ele está funcionando porque você não precisa estar autorizado de forma alguma para fazer a requisição GET. Nós nem sequer verificamos o cabeçalho Authorization se você o enviar. Requisições GET para endpoints públicos funcionarão corretamente sem nenhum cabeçalho.

@pedroleaoc aqui está um pequeno script de demonstração em Python que mostra como fazer requisições autenticadas e como enviar dados de requisição para requisições PUT/POST.

# discourse-api-demo.py
import requests
from requests.structures import CaseInsensitiveDict

# Requisição GET básica para uma URL pública, sem necessidade de cabeçalhos.
url = "http://localhost:3000/posts/10.json"

resp = requests.get(url)

print(resp.status_code)
print(resp.content)

# Requisição GET para um endpoint privado. Cabeçalhos de autenticação são necessários.
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)

# Requisição PUT com um corpo de requisição
url = "http://localhost:3000/posts/10.json"
data = { 'raw': "Post legal, mas aqui está uma atualização do corpo do post", 'edit_reason': "Mudei isso porque posso." }

resp = requests.put(url, headers=headers, json=data)
print(resp.status_code)
print(resp.content)

De Quickstart — Requests 2.33.1 documentation

Em vez de codificar o dict você mesmo, você também pode passá-lo diretamente usando o parâmetro json (adicionado na versão 2.4.2) e ele será codificado automaticamente:

url = 'https://api.github.com/some/endpoint' >>> payload = {'some': 'data'} >>> r = requests.post(url, json=payload)

Nota: o parâmetro json é ignorado se data ou files for passado.

Usar o parâmetro json na requisição alterará o Content-Type no cabeçalho para application/json.

Doh, claro! Nem todos os GETs exigem autorização!
Isso é uma dica muito útil, vou investigar isso. Obrigado!

Coisas que eu já sei por engenharia reversa:

  • api-key em vez de api_key
  • A requisição PUT exige "content-type": "application/x-www-form-urlencoded"
  • Os dados não estão em JSON (estão codificados, embora eu não pareça encontrar a codificação correta)

Veja minha postagem logo acima desta. Você pode usar o formato json=data em vez de data=data na sua requisição, e a biblioteca requests do Python cuidará do content-type para você, definindo-o como application/json, que é o que você deve estar usando.

Ótimo, agora consigo editar minha postagem! Obrigado pela ajuda!

Só há mais um problema: a chave que estou usando é global. Quando tento com uma chave que tem apenas permissões de write e read, recebo a mensagem: Você não tem permissão para visualizar o recurso solicitado. O nome de usuário ou a chave da API é inválida.. Ao editar a postagem pela interface gráfica, parece haver uma requisição PUT para a URL do tópico (além daquela que realmente edita a postagem posts/post_id.json), a qual não consigo reproduzir com uma chave de API limitada, apenas com a global. Não vejo, no entanto, por que eu não conseguiria editar a postagem via API mesmo sem essa requisição PUT extra que ocorre na interface gráfica.

EDIT: Tecnicamente, minha chave de API cobre /t/:slug/:topic_id, que é para onde a requisição PUT está apontando.

Você tem o escopo “editar Posts” selecionado para sua chave de API?

Não! Eu nem mesmo tenho essa opção! Vou verificar isso, obrigado pela ajuda mais uma vez.