게시글을 수정할 때 올바른 `content-type`은 무엇인가요?

여러분, API를 통해 게시글을 업데이트할 때 Content-Type으로 application/json을 사용할 수 있을까요? 문서에는 가능하다고 나와 있지만, 불가능할지도 모르겠다는 생각이 들기 시작했습니다.

항상 ["BAD CSRF"] 오류가 발생하는데, 이게 무슨 의미인지 전혀 모르겠습니다.


multipart/form-data를 사용해야 한다면, PUT 요청을 어떻게 구성해야 하는지, 특히 data 부분을 어떻게 만들어야 하는지에 대한 조언을 주실 수 있을까요?

header = CaseInsensitiveDict()
header["Authorization"] = '{"api-key": "longapikey", "api-username": "myusername"}'
{
  "post": {
      "raw": "Cool post, but here's an updated to the post's body",
      "edit_reason": "I changed this because I can."
   }
}

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

감사합니다!

Authorization 헤더가 아닌, Api-Key와 Api-Username을 각각 별도의 헤더로 보내야 합니다. 다음과 같은 방식으로 하면 더 잘 작동할 것입니다:

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

이 문제는 이전 주제와 동일한 것으로 보입니다:

답장 주셔서 감사합니다, David.

말씀하신 대로 헤더를 포맷하면 다음 같은 결과가 나와서 정말 이상합니다:

{"errors":["You are not permitted to view the requested resource. The API username or key is invalid."],"error_type":"invalid_access"}

["Authorization"]를 추가하면 작동합니다.

이것은 단순한 GET 요청에 대한 것입니다. 여전히 PUT은 수행할 수 없습니다.

특정 연동에서 작동하는 헤더는 모든 연동에서 작동할 것이라고 기대했습니다(키가 global인 경우 - 실제로도 그렇습니다). 그래서 지금은 헤더에 대해 크게 걱정하지 않고 있습니다 - 걱정해야 하나요?

감사합니다!

GET 요청이 작동하는 반면 Authorization 헤더를 잘못된 방식으로 사용하면서 PUT 요청이 작동하지 않는다면, 아마도 PUT 요청에 어떤 형식 오류가 있는 것 같습니다. GET 요청은 인증이 전혀 필요 없기 때문에 작동하는 것입니다. Authorization 헤더를 전달하더라도 우리는 이를 확인하지 않습니다. 공개 엔드포인트에 대한 GET 요청은 헤더 없이도 정상적으로 작동합니다.

@pedroleaoc 인증된 요청을 보내고 put/post 요청의 요청 데이터를 전송하는 방법을 보여주는 간단한 파이썬 스크립트 샘플입니다.

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

# 공개 URL에 대한 기본 GET 요청, 헤더 불필요.
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': "Cool post, but here's an updated to the post's body", 'edit_reason': "I changed this because I can." }

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

Quickstart — Requests 2.34.2 documentation 에서 가져옴

dict를 직접 인코딩하는 대신, json 매개변수를 사용하여 직접 전달할 수도 있습니다(2.4.2 버전에서 추가됨). 이렇게 하면 자동으로 인코딩됩니다:

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

참고: data 또는 files 중 하나가 전달되면 json 매개변수는 무시됩니다.

요청에서 json 매개변수를 사용하면 헤더의 Content-Type이 application/json으로 변경됩니다.

아, 물론이죠! 모든 GET 요청에 Authorization이 필요한 건 아니었네요!
정말 좋은 지적 감사합니다. 한번 살펴보겠습니다.

역공학으로 이미 파악한 내용들:

  • api-key를 api_key 대신 사용해야 함
  • PUT 요청에는 "content-type": "application/x-www-form-urlencoded"가 필요함
  • 데이터가 JSON 형식이 아님 (인코딩되어 있긴 한데, 올바른 인코딩 방식을 찾지 못하고 있음)

이 바로 위에 있는 제 게시물을 참고해 주세요. 요청에서 data=data 형식 대신 json=data를 사용하면 python requests 라이브러리가 content-type을 자동으로 처리하여 application/json으로 설정해 줍니다. 이것이 사용해야 할 올바른 형식입니다.

좋아요, 이제 내 게시물을 수정할 수 있습니다! 도움 주셔서 감사합니다!

다만 한 가지 문제가 더 있습니다. 제가 사용 중인 키는 global입니다. write와 read 권한만 있는 키로 시도하면 You are not permitted to view the requested resource. The API username or key is invalid.라는 오류가 발생합니다. GUI를 통해 게시물을 수정하면 실제로 게시물을 수정하는 posts/post_id.json 요청 외에 해당 토픽 URL로 PUT 요청이 하나 더 보내지는 것 같은데, 이 요청은 제한된 API 키로는 재현할 수 없고, global 키로만 가능합니다. 하지만 GUI에서 발생하는 이 추가 PUT 요청이 없더라도 API를 통해 게시물을 수정할 수 없는 이유는 무엇인지 이해가 되지 않습니다.

수정: 기술적으로 제 API 키는 PUT 요청이 향하는 /t/:slug/:topic_id를 포함합니다.

API 키에 “게시물 편집” 범위가 선택되어 있나요?

아니요! 그런 옵션이 아예 없어요! 알아볼게요. 또 도움 주셔서 감사합니다.