garret

[Python] requests, MultipartEncoder 라이브러리로 REST API 호출 본문

Programming/Python

[Python] requests, MultipartEncoder 라이브러리로 REST API 호출

_Sun_ 2023. 8. 23. 18:13

이전까진 postman을 통한 rest api 호출로 값을 확인했었다.

하지만, 최근 실제 rest api를 python으로 호출해 사용할 일이 생겨서 REST API 호출하는 과정을 시행착오 포함해 모두 정리해보았다. 

 

 

앞 2가지는 실패한 것들의 원인을 적어놓았고, 마지막은 성공한 코드를 적어놓았으니,

급한 사람은 마지막 시도를 참고할 것. 

 

 

 

 

 

첫번째 시도 [실패]


 

구글링해서 가장 먼저 나온 블로그를 참고해서 아래처럼 시도해보았다. 

#예시 코드
import requests
import json

def send_api(path, method):
    API_HOST = "http://www.example.com"
    url = API_HOST + path
    headers = {
    	'Authorization': 'Basic #####',
        'Username': 'aaa',
        'Password': '1234',
    	'Content-Type': 'application/json'}
    body = {
        "key1": "value1"
    }
    
    try:
        if method == 'GET':
            response = requests.get(url, headers=headers)
        elif method == 'POST':
            response = requests.post(url, headers=headers, data=json.dumps(body, ensure_ascii=False, indent="\t"))
        print("response status %r" % response.status_code)
        print("response text %r" % response.text)
    except Exception as ex:
        print(ex)
  

# 호출 예시
send_api("/test", "POST")

 

400 에러가 발생하면서 response가 안오는 오류 발생

 

 

 

 

두번째 시도 [실패]


포스트맨을 확인해보니, Content-Typemultipart/form-data;로 되어 있는 것을 확인. 

 

 

 

multipart/form-data를 처리하기 위해서는

requests_toolbelt.multipart.encoder 라이브러리의 MultipartEncoder가 필요하다.  

 

import requests
import json
from requests_toolbelt.multipart.encoder import MultipartEncoder

def send_api(path, method):
    API_HOST = "http://www.example.com"
    url = API_HOST + path
    headers = {
        'Authorization':'Basic #####',
        'Username': 'aaa',
        'Password':'1234',
        'Content-Type': 'multipart/form-data; boundary=<calculated when request is sent>'
    }
    multipart_data = MultipartEncoder(
        fields={
            'key': 'value'
        }
    )
    try:
        if method == 'GET':
            response = requests.get(url, headers=headers)
        elif method == 'POST':
            response = requests.post(url, headers=headers, data=multipart_data)
        print("response status %r" % response.status_code)
        print("response text %r" % response.text)
    except Exception as ex:
        print(ex)
  

# 호출 예시
send_api("/test", "POST")

 

MultipartEncoder 추가 결과

내가 입력한 value값이 아니라, value 값에 '\\r\\n--#####' 이상한 string이 추가로 붙어서 response가 안오는 오류 발생

 

 

 

이 string이 왜 붙어서 출력되는지 아무리 검색해도 원인을 찾을 수 없어서, chatgpt한테 마지막으로 물어봤더니, 

MultipartEncoder의 delimiter일 수도 있다는 답변이 나왔다!!! (Thanks chatgpt!!)

 

 

 

즉, MultipartEncoder를 사용할 때,

boundary 매개변수를 설정하여 delimiter 문자열을 지정하지 않으면,

무작위로 생성된 delimiter 문자열이 사용된다고 한다. 

 

 

 

세번째 시도 [성공!]


 

Content-Type에 지정된 boundary를 MultipartEncoder의 boundary 매개변수에 똑같이 작성해주었다. 

import requests
import json
from requests_toolbelt.multipart.encoder import MultipartEncoder

def send_api(path, method):
    API_HOST = "http://www.example.com"
    url = API_HOST + path
    headers = {
        'Authorization':'Basic #######',
        'Username': 'aaa',
        'Password':'1234',
        'Content-Type': 'multipart/form-data; boundary=<calculated when request is sent>'
    }
    multipart_data = MultipartEncoder(
        fields={
            'key': 'value'
        }, boundary = '<calculated when request is sent>'
    )
    try:
        if method == 'GET':
            response = requests.get(url, headers=headers)
        elif method == 'POST':
            response = requests.post(url, headers=headers, data=multipart_data)
        print("response status %r" % response.status_code)
        print("response text %r" % response.text)
    except Exception as ex:
        print(ex)
  

# 호출 예시
send_api("/test", "POST")

 

이랬더니 REST API 호출 성공!

 

 

 

결론


header의 ‘Content-Type’이 ‘multipart/form-data’로 되어 있으면 MultipartEncoder를 사용해야 하고, 이때 boundary매개변수에 delimiter 지정해줘야 한다. 

 

 

 

 

 

Reference


https://toramko.tistory.com/entry/python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-requests-%EB%A1%9C-REST-API-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0