2021년 10월 3일 일요일

python post/get requests( 파이썬에서 requests를 이용한 POST/GET requests시 내용 보기 )

POST/GET 은 클라이언트가 서버에 web page를 요청하는 방법입니다. 

즉 우리가 http://128.0.0.1/ 이라고 typing 하면 여기까지는 URL 이 되고, 인자를 어떻게 보낼지 결정하는것이 POST / GET 입니다.


1. GET

URL에 변수(데이터)를 포함시켜 요청합니다.

예를들어 보겠습니다. 웹에 로그인 페이지가 있고 id와 pw를 입력받는다고 할때 GET에서는 아래 방식으로 URL에 해당 정보를 포함할 수 있습니다. URL에 정보가 노출 되는 방식 입니다.

http://128.0.0.1/login?id=test&pw=go


2. POST

http://128.0.0.1/login 이렇게 URL을 요청하고

나머지 id=test , pw=go 는 다른 방식으로 전달합니다.

방법은 좀 더 다양하고 복잡합니다. 왜냐하면 읽는쪽에서 이것을 어떻게 읽을거냐에 대한 약속이 필요하기 때문입니다.


방법은 많이들 알고 있는데 이것 두개가 항상 항상 헷갈립니다. 그냥 외웁시다. GET은 URL을 변형해서 요청 한다... ^^;


여기에서는 python requests 모듈과 flask 를 이용한 서버 및 테스트 하면서 정리해 보겠습니다.


3. Flask 사용

웹서버 형태로도 시험을 해도 되지만 http의 GET / POST 을 처리할수 있기만 하면 되어서, 간단하게 UI도 없는 형태라 flask가 적당해서 구현하였습니다. 


flask로 만든 서버 소스

from flask import Flask, json, request

funs1 = [{"id": 1, "name": "fun1"}, {"id": 2, "name": "fun2"}]
funs2 = [{"id": 3, "name": "fun1"}, {"id": 4, "name": "fun2"}]

api = Flask(__name__)

@api.route('/funs', methods=['GET'])
def get_funs():
	print("get request.args:",request.args.to_dict())
	print("get request.json:",request.json)
	print("get request.form:",request.form.to_dict())
	print("get request.getdata:",request.get_data())
	return json.dumps(funs1), 200

@api.route('/funs', methods=['POST'])
def post_funs():
	print("post request.args:",request.args.to_dict())
	print("post request.json:",request.json)
	print("post request.form:",request.form.to_dict())
	print("post request.getdata:",request.get_data())
	return json.dumps(funs2), 201

if __name__ == '__main__':
    api.run(debug=True)


flask의 /funs URL을 접속할때 post, get 등이 어떠한 값형태를 취하는지 구현하였습니다.

이때 201,200 을 취하도록 처리하였기 때문에(함수의 리턴 부분 참고) 해당 값에 대해서는 특별히 주의 하지 않아도 됩니다.(이말은 보통 http의경우 200이 오류 없음을 나타내는데 201이 와도 특별하게 처리를 안해줘도 된다는 의미입니다. 여기에서는 단순히 구별하는 용도록 차이를 두었습니다.)


4. requests로 만드는 post, get

request의 post, get함수가 있는데 뒤쪽의 params, data, json 인자에 대해서 확인이 필요하기 때문에, 각각 post/get 에 대하여 여러가지 방식으로 전달하도록 구현해보았습니다.

import requests

def print_roundtrip(response, *args, **kwargs):
	format_headers = lambda d: '\n'.join(f'{k}: {v}' for k, v in d.items())
	print((""
		"---------------- request ----------------\n"
		"{req.method} {req.url}\n"
		"{reqhdrs}\n"
		"\n"
		"{req.body}\n"
		"---------------- response ----------------\n"
		"{res.status_code} {res.reason} {res.url}\n"
		"{reshdrs}\n"
		"\n"
		"{res.text}\n"
		"").format(
		req=response.request, 
		res=response, 
		reqhdrs=format_headers(response.request.headers), 
		reshdrs=format_headers(response.headers), 
	))

params = {'param1':'v1','param2':2}
params['param0']=1
print("requests.get('http://127.0.0.1:5000/funs', params=params")
resp = requests.get('http://127.0.0.1:5000/funs', params=params, hooks={'response': print_roundtrip})
print("**************")
params['param0']=2
print("requests.get('http://127.0.0.1:5000/funs', data=params")
resp = requests.get('http://127.0.0.1:5000/funs', data=params, hooks={'response': print_roundtrip})
print("**************")
params['param0']=3
print("requests.post('http://127.0.0.1:5000/funs', params=params")
resp = requests.post('http://127.0.0.1:5000/funs', params=params, hooks={'response': print_roundtrip})
print("**************")
params['param0']=4
print("requests.post('http://127.0.0.1:5000/funs', data=params")
resp = requests.post('http://127.0.0.1:5000/funs', data=params, hooks={'response': print_roundtrip})
print("**************")
params['param0']=5
print("requests.post('http://127.0.0.1:5000/funs', json=params")
resp = requests.post('http://127.0.0.1:5000/funs', json=params, hooks={'response': print_roundtrip})
print("**************")

그전에 requests의 주고받는 데이터 정보 디버깅용으로 출력할 필요가 있는데, 그것이 hooks를 이용하는 방법입니다.

위에서는 hooks={'response': print_roundtrip} 부분을 참고하면 됩니다.


5. 실행 결과

서버쪽 실행결과

C:\Users\USER\Documents\GitHub\sourcecode\python\example>python _18_requests_get_post_server.py
 * Serving Flask app "_18_requests_get_post_server" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 298-679-048
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
get request.args: {'param1': 'v1', 'param2': '2', 'param0': '1'}
get request.json: None
get request.form: {}
get request.getdata: b''
127.0.0.1 - - [03/Oct/2021 13:47:46] "GET /funs?param1=v1&param2=2&param0=1 HTTP/1.1" 200 -
get request.args: {}
get request.json: None
get request.form: {'param1': 'v1', 'param2': '2', 'param0': '2'}
get request.getdata: b''
127.0.0.1 - - [03/Oct/2021 13:47:46] "GET /funs HTTP/1.1" 200 -
post request.args: {'param1': 'v1', 'param2': '2', 'param0': '3'}
post request.json: None
post request.form: {}
post request.getdata: b''
127.0.0.1 - - [03/Oct/2021 13:47:46] "POST /funs?param1=v1&param2=2&param0=3 HTTP/1.1" 201 -
post request.args: {}
post request.json: None
post request.form: {'param1': 'v1', 'param2': '2', 'param0': '4'}
post request.getdata: b''
127.0.0.1 - - [03/Oct/2021 13:47:46] "POST /funs HTTP/1.1" 201 -
post request.args: {}
post request.json: {'param1': 'v1', 'param2': 2, 'param0': 5}
post request.form: {}
post request.getdata: b'{"param1": "v1", "param2": 2, "param0": 5}'
127.0.0.1 - - [03/Oct/2021 13:47:46] "POST /funs HTTP/1.1" 201 -


클라이언트쪽 실행 결과

C:\Users\USER\Documents\GitHub\sourcecode\python\example>python _18_requests_get_post.py
requests.get('http://127.0.0.1:5000/funs', params=params
---------------- request ----------------
GET http://127.0.0.1:5000/funs?param1=v1&param2=2&param0=1
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

None
---------------- response ----------------
200 OK http://127.0.0.1:5000/funs?param1=v1&param2=2&param0=1
Content-Type: text/html; charset=utf-8
Content-Length: 54
Server: Werkzeug/1.0.1 Python/3.8.3
Date: Sun, 03 Oct 2021 04:47:46 GMT

[{"id": 1, "name": "fun1"}, {"id": 2, "name": "fun2"}]

**************
requests.get('http://127.0.0.1:5000/funs', data=params
---------------- request ----------------
GET http://127.0.0.1:5000/funs
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 27
Content-Type: application/x-www-form-urlencoded

param1=v1&param2=2&param0=2
---------------- response ----------------
200 OK http://127.0.0.1:5000/funs
Content-Type: text/html; charset=utf-8
Content-Length: 54
Server: Werkzeug/1.0.1 Python/3.8.3
Date: Sun, 03 Oct 2021 04:47:46 GMT

[{"id": 1, "name": "fun1"}, {"id": 2, "name": "fun2"}]

**************
requests.post('http://127.0.0.1:5000/funs', params=params
---------------- request ----------------
POST http://127.0.0.1:5000/funs?param1=v1&param2=2&param0=3
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 0

None
---------------- response ----------------
201 CREATED http://127.0.0.1:5000/funs?param1=v1&param2=2&param0=3
Content-Type: text/html; charset=utf-8
Content-Length: 54
Server: Werkzeug/1.0.1 Python/3.8.3
Date: Sun, 03 Oct 2021 04:47:46 GMT

[{"id": 3, "name": "fun1"}, {"id": 4, "name": "fun2"}]

**************
requests.post('http://127.0.0.1:5000/funs', data=params
---------------- request ----------------
POST http://127.0.0.1:5000/funs
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 27
Content-Type: application/x-www-form-urlencoded

param1=v1&param2=2&param0=4
---------------- response ----------------
201 CREATED http://127.0.0.1:5000/funs
Content-Type: text/html; charset=utf-8
Content-Length: 54
Server: Werkzeug/1.0.1 Python/3.8.3
Date: Sun, 03 Oct 2021 04:47:46 GMT

[{"id": 3, "name": "fun1"}, {"id": 4, "name": "fun2"}]

**************
requests.post('http://127.0.0.1:5000/funs', json=params
---------------- request ----------------
POST http://127.0.0.1:5000/funs
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 42
Content-Type: application/json

b'{"param1": "v1", "param2": 2, "param0": 5}'
---------------- response ----------------
201 CREATED http://127.0.0.1:5000/funs
Content-Type: text/html; charset=utf-8
Content-Length: 54
Server: Werkzeug/1.0.1 Python/3.8.3
Date: Sun, 03 Oct 2021 04:47:46 GMT

[{"id": 3, "name": "fun1"}, {"id": 4, "name": "fun2"}]

**************


6. 정리 및 결론

post, get과 관계없이 params 으로 넘기냐 data로 넘기느냐 web 방식의 차이가 있음

GET/POST 이부분은 request할때 아래 부분이 달라집니다.

---------------- request ----------------

POST http://127.0.0.1:5000/funs

---------------- request ----------------

GET http://127.0.0.1:5000/funs

이러한 사실은 서버쪽에서 처리하는 방식의 차이로 이루어집니다.

GET/POST 관계 없이 params=params 의 경우 params 데이터를 이용하여 자체적으로 url뒤쪽에 데이터를 만들게 됩니다.

즉 3번째 POST 임에도 params를 사용하는 예제의 결과 참고

resp = requests.post('http://127.0.0.1:5000/funs', params=params, hooks={'response': print_roundtrip})

결과는 param으로 들어온 인자가 GET에서 사용하는 URL 형태로 변형됩니다.

POST http://127.0.0.1:5000/funs?param1=v1&param2=2&param0=3


GET/POST 관계 없이 data=params 를 사용하게 되면 아래 형태로 전달하게 됩니다.

Content-Type: application/x-www-form-urlencoded

2/4번 예제참고

json을 사용하게 되면 Content-Type: application/json 이용하여 전달하게 됩니다.

서버측 분석을 결과로 볼때 Content-Type 에따라서 읽는 방식이 달라집니다.


7. 총정리

requests 사용 인자에 따른 분류

params 사용시

    URL 사용 

    Content 사용안함

    서버 request.args 로 읽어야함

    GET 사용시 사용해야함

json 사용시

    Content로 전달

    Content-Type: application/json

    서버 request.json , request.get_data() 읽음

    서버가 application/json 데이터를 받을 수 있을때 사용

    POST 로 사용가능

data 사용시

    Content로 전달 

    Content-Type: application/x-www-form-urlencoded

    form의 전송 버튼 사용시 사용함

    서버 request.get_data() 읽음

    POST 로 사용가능





댓글 없음:

댓글 쓰기