2021년 10월 7일 목요일

python REST API (2) 예제

REST API 기본 설명

https://swlock.blogspot.com/2021/03/python-rest-api.html



지난 글에서는 REST API POST/GET 예제로만 만들었습니다.

이번에는 좀 더 복잡한 예제를 만들었는데

REST API 주고받는 규격 부분은 서버 측과 약속이 필요한 부분입니다.

여기 예제에서는 json 으로 처리했으며 이 부분은 다른 서버와는 다를 수 있습니다.


Flask 서버로 만든 예제입니다.


/stock/<상품> 이런형태로 여기에 POST/PUT/GET/PATCH/DELETE가 이루어집니다.

각각의 기본 설명은 이전 설명을 참고하세요.

from flask import Flask, make_response, jsonify, request

stock = {
	"fruit": {
		"apple": 10,
		"banana": 20
	}
}

app = Flask(__name__)

@app.route("/stock")
def get_stock():
	res = make_response(jsonify(stock), 200)
	return res
 
@app.route("/stock/<goods>")
def get_goods(goods):
	""" Returns a goods from stock """
	if goods in stock:
		res = make_response(jsonify(stock[goods]), 200)
		return res
	res = res = make_response(jsonify({"error": "Not found"}), 404)
	return res

@app.route("/stock/<goods>", methods=["POST"])
def create_goods(goods):
	""" Creates a new goods if it doesn't exist """
	req = request.get_json()
	if goods in stock:
		res = make_response(jsonify({"error": "goods already exists"}), 400)
		return res
	stock.update({goods: req})
	res = make_response(jsonify({"message": "goods created"}), 201)
	return res
	
@app.route("/stock/<goods>", methods=["PUT"])
def put_goods(goods):
	""" Replaces or creates a goods """
	req = request.get_json()
	if goods in stock:
		stock[goods] = req
		res = make_response(jsonify({"message": "goods replaced"}), 200)
		return res
	stock[goods] = req
	res = make_response(jsonify({"message": "goods created"}), 201)
	return res
 
@app.route("/stock/<goods>", methods=["PATCH"])
def patch_goods(goods):
	""" Updates or creates a goods """
	req = request.get_json()
	if goods in stock:
		for k, v in req.items():
			stock[goods][k] = v
		res = make_response(jsonify({"message": "goods updated"}), 200)
		return res
	stock[goods] = req
	res = make_response(jsonify({"message": "goods created"}), 201)
	return res

@app.route("/stock/<goods>", methods=["DELETE"])
def delete_goods(goods):
	""" If the goods exists, delete it """
	if goods in stock:
		del stock[goods]
		res = make_response(jsonify({}), 204)
		return res

	res = make_response(jsonify({"error": "goods not found"}), 404)
	return res

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


client 예제입니다.

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), 
	))

data1={"cat":1,"dog":2}
data2={"cat":3,"lion":4,"bird":2}
data3={"cat":3,"dog":4}

resp = requests.get('http://127.0.0.1:5000/stock', hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock/fruit', hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock/animal', hooks={'response': print_roundtrip})

resp = requests.post('http://127.0.0.1:5000/stock/animal', json=data1, hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock', hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock/animal', hooks={'response': print_roundtrip})

resp = requests.put('http://127.0.0.1:5000/stock/animal', json=data2, hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock/animal', hooks={'response': print_roundtrip})

resp = requests.patch('http://127.0.0.1:5000/stock/animal', json=data3, hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock/animal', hooks={'response': print_roundtrip})

resp = requests.delete('http://127.0.0.1:5000/stock/animal', hooks={'response': print_roundtrip})

resp = requests.get('http://127.0.0.1:5000/stock/animal', hooks={'response': print_roundtrip})

print_roundtrip 함수는 로그 출력을 위한 부분입니다.

순서대로 결과를 보면서 각각 어떻게 동작하는지 살펴 보도록 합시다.


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

GET http://127.0.0.1:5000/stock

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/stock

Content-Type: application/json

Content-Length: 56

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "fruit": {

    "apple": 10,

    "banana": 20

  }

}

설명

처음입니다. GET http://127.0.0.1:5000/stock 을 했기 때문에 서버에 존재하는 기본 fruit의 값이 넘어왔습니다.


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

GET http://127.0.0.1:5000/stock/fruit

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/stock/fruit

Content-Type: application/json

Content-Length: 35

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "apple": 10,

  "banana": 20

}

설명

GET http://127.0.0.1:5000/stock/fruit 이 동작입니다. fruit안에 있는 값을 넘겨 받습니다.


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

GET http://127.0.0.1:5000/stock/animal

User-Agent: python-requests/2.23.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive


None

---------------- response ----------------

404 NOT FOUND http://127.0.0.1:5000/stock/animal

Content-Type: application/json

Content-Length: 27

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "error": "Not found"

}

설명

GET http://127.0.0.1:5000/stock/animal 입니다. 이동작은 animal이란곳에는 아무런 데이터가 없기 때문에 404에러가 리턴됩니다.


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

POST http://127.0.0.1:5000/stock/animal

User-Agent: python-requests/2.23.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Content-Length: 20

Content-Type: application/json


b'{"cat": 1, "dog": 2}'

---------------- response ----------------

201 CREATED http://127.0.0.1:5000/stock/animal

Content-Type: application/json

Content-Length: 33

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "message": "goods created"

}

설명

animal에 없는걸 알았으니 이번에는 넣을 차례입니다. 

POST http://127.0.0.1:5000/stock/animal 여기에 b'{"cat": 1, "dog": 2}' 이값을 넣습니다.


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

GET http://127.0.0.1:5000/stock

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/stock

Content-Type: application/json

Content-Length: 104

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "animal": {

    "cat": 1,

    "dog": 2

  },

  "fruit": {

    "apple": 10,

    "banana": 20

  }

}

설명

전체 값을 읽어보면 animal,fruit 모두 나옵니다.


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

GET http://127.0.0.1:5000/stock/animal

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/stock/animal

Content-Type: application/json

Content-Length: 28

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "cat": 1,

  "dog": 2

}

설명

구체적으로 animal 만 GET할 수도 있습니다.


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

PUT http://127.0.0.1:5000/stock/animal

User-Agent: python-requests/2.23.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Content-Length: 32

Content-Type: application/json


b'{"cat": 3, "lion": 4, "bird": 2}'

---------------- response ----------------

200 OK http://127.0.0.1:5000/stock/animal

Content-Type: application/json

Content-Length: 34

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "message": "goods replaced"

}

설명

이번엔 PUT입니다. 이건 존재하는 값을 교체하는 것입니다. 즉 animal 전체가 교체됩니다.


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

GET http://127.0.0.1:5000/stock/animal

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/stock/animal

Content-Type: application/json

Content-Length: 43

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "bird": 2,

  "cat": 3,

  "lion": 4

}

설명

교체한 값을 읽어봅니다. PUT으로 넣은값이 잘들어가 있습니다.


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

PATCH http://127.0.0.1:5000/stock/animal

User-Agent: python-requests/2.23.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Content-Length: 20

Content-Type: application/json


b'{"cat": 3, "dog": 4}'

---------------- response ----------------

200 OK http://127.0.0.1:5000/stock/animal

Content-Type: application/json

Content-Length: 33

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "message": "goods updated"

}

설명

PATCH는 PUT과 비슷하긴한데 전체 교체가 아니라 있는 아이템만 교체하거나 추가됩니다. 그래서 삭제는 이루어 지지 않습니다.


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

GET http://127.0.0.1:5000/stock/animal

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/stock/animal

Content-Type: application/json

Content-Length: 56

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "bird": 2,

  "cat": 3,

  "dog": 4,

  "lion": 4

}

설명

다시 읽어보면 추가되거나 교체한 항목이 존재합니다.


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

DELETE http://127.0.0.1:5000/stock/animal

User-Agent: python-requests/2.23.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Content-Length: 0


None

---------------- response ----------------

204 NO CONTENT http://127.0.0.1:5000/stock/animal

Content-Type: application/json

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


설명

DELTE는 animal 데이터를 삭제합니다.


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

GET http://127.0.0.1:5000/stock/animal

User-Agent: python-requests/2.23.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive


None

---------------- response ----------------

404 NOT FOUND http://127.0.0.1:5000/stock/animal

Content-Type: application/json

Content-Length: 27

Server: Werkzeug/1.0.1 Python/3.8.3

Date: Wed, 06 Oct 2021 23:09:55 GMT


{

  "error": "Not found"

}

설명

삭제한것을 읽어보면 404 에러가 발생합니다. 정상적으로 삭제되었습니다.


예제를 하나씩 따라하다보면 REST API가 이해가 되리라 생각됩니다.





댓글 없음:

댓글 쓰기