Building beautiful REST APIs using Flask, Swagger UI and Flask-RESTPlus > 파이썬

파이썬

그누 어디까지 써봤니? 나는 파이썬까지 써봤어!

Building beautiful REST APIs using Flask, Swagger UI and Flask-RESTPlus 정보

Building beautiful REST APIs using Flask, Swagger UI and Flask-RESTPlus

본문

이 기사에서는 Flask 및 Flask-RESTPlus 를 사용하여 REST API 를 작성하는 데 필요한 단계를 설명합니다. 이러한 도구는 프레임 워크로 결합되어 일반적인 작업을 자동화합니다.

  • API 입력 검증
  • 형식화 출력 (JSON 으로)
  • 대화식 문서 생성 (Swagger UI 사용)
  • 파이썬 예외를 기계가 읽을 수 있는 HTTP 응답으로 바꾸기

Flask

Flask 는 Python 으로 작성된 웹 마이크로 프레임 워크입니다. 마이크로 프레임 워크이기 때문에 플라스크는 그 자체로 거의 수행하지 않습니다. "배터리 포함"방식을 채택한 Django 와 같은 프레임 워크와 달리 Flask 에는 ORM, 시리얼 라이저, 사용자 관리 또는 기본 제공 국제화가 제공되지 않습니다. 이러한 모든 기능 및 기타 여러 기능은 Flask 확장 기능으로 사용할 수 있으며 풍부하지만 느슨하게 결합 된 에코 시스템을 구성합니다.

따라서 주목받는 Flask 개발자의 과제는 올바른 확장 기능을 선택하고이를 결합하여 올바른 기능 세트를 얻는 것입니다. 이 기사에서는 Flask-RESTPlus 확장을 사용하여 Flask 기반 RESTFul JSON API 를 작성하는 방법에 대해 설명합니다.

Flask-RESTPlus

Flask-RESTPlus 는 REST API 를 빠르고 쉽게 구축하는 것을 목표로합니다. 코드를 읽기 쉽고 유지 관리하기에 충분한 구문 설탕을 제공합니다. 킬러 기능은 Swagger UI 를 사용하여 API 에 대한 대화식 문서를 자동으로 생성하는 기능입니다.

Swagger UI

Swagger UI 는 RESTFul 웹 서비스를 문서화하기위한 일련의 기술 중 하나입니다. Swagger 는 현재 Linux Foundation 에서 선별 한 OpenAPI 사양으로 발전했습니다. 웹 서비스에 대한 OpenAPI 설명이 있으면 소프트웨어 도구를 사용하여 다양한 언어로 문서 또는 상용구 코드 (클라이언트 또는 서버)를 생성 할 수 있습니다. 자세한 내용은 swagger.io 를 참조하십시오.

Swagger UI 는 RESTFul 웹 서비스를 설명하고 시각화하는 데 유용한 도구입니다. API 를 문서화하고 JavaScript 를 사용하여 테스트 쿼리를 작성할 수있는 작은 웹 페이지를 생성합니다. 작은 데모를 보려면 여기를 클릭하십시오.

이 기사에서는 Flask 및 Flask-RESTPlus 를 사용하여 Swagger UI 가 장착 된 RESTFul API 를 작성하는 방법에 대해 설명합니다.

Getting started

Flask-RESTPlus 의 기능을 보여주기 위해 작은 데모 응용 프로그램을 준비했습니다. 블로그 게시물 및 카테고리를 관리 할 수 있는 블로그 플랫폼 용 API 의 일부입니다.

시스템에서 이 데모를 다운로드하여 실행 해 보도록 하겠습니다. 그러면 코드를 살펴 보겠습니다.

Prerequisites

컴퓨터에 Virtualenv 및 Git 이 포함 된 Python 이 설치되어 있어야합니다.

Python 3 을 사용하는 것이 좋지만 Python 2 는 정상적으로 작동합니다.

Setting up the demo application

데모 애플리케이션을 다운로드하여 시작하려면 다음 명령을 실행하십시오. 먼저 응용 프로그램 코드를 디스크의 임의의 디렉토리에 복제하십시오.

$ cd /path/to/my/workspace/
$ git clone https://github.com/postrational/rest_api_demo
$ cd rest_api_demo

venv 라는 디렉토리에 가상 Python 환경을 작성하고 virtualenv 를 활성화 한 후 pip 를 사용하여 필수 종속성을 설치하십시오.

$ virtualenv -p `which python3` venv
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt

이제 개발 용 앱을 설정하고 시작해 보겠습니다.

(venv) $ python setup.py develop
(venv) $ python rest_api_demo/app.py

자, 모든 준비가되어 있어야합니다. 브라우저에서 URL http : // localhost : 8888 / API /를여십시오.

다음과 유사한 페이지가 표시됩니다.

restplusapilistingmethods.png

Defining your Flask app and RESTPlus API

Flask 및 Flask-RESTPlus 를 사용하면 쉽게 시작할 수 있습니다. 작동하는 API 를 작성하는 데 필요한 최소 코드는 10 줄입니다.

from flask import Flask 
from flask_restplus import Resource, Api 

app = Flask(__name__) 		# Create a Flask WSGI application 
api = Api(app) 				# Create a Flask-RESTPlus API 

@api.route('/hello') 			# Create a URL route to this resource 
class HelloWorld(Resource): 	# Create a RESTful resource 
	def get(self): 			# Create GET endpoint 
		return {'hello': 'world'} 

if __name__ == '__main__': 
	app.run(debug=True) 	# Start a development server

코드를보다 유지 보수하기 쉽게하기 위해 데모 애플리케이션에서 앱 정의, API 메소드 및 기타 유형의 코드를 별도의 파일로 분리합니다. 다음 디렉토리 트리는 로직의 각 부분이있는 위치를 보여줍니다.

├── api                         		#
│   ├── blog                    		#  Blog-related API directory
│   │   ├── business.py         	#
│   │   ├── endpoints           	#  API namespaces and REST methods
│   │   │   ├── categories.py   	#
│   │   │   └── posts.py        	#
│   │   ├── parsers.py          	#  Argument parsers
│   │   └── serializers.py      	#  Output serializers
│   └── restplus.py             	#  API bootstrap file
├── app.py                      		#  Application bootstrap file
├── database                    	#
│   └── models.py               	#  Definition of SQLAlchemy models
├── db.sqlite                   		#
└── settings.py                 	#  Global app settings

RESTPlus API 의 정의는 rest_API_demo / API / restplus.py 파일에 저장되는 반면 Flask 앱을 구성하고 시작하는 로직은 rest_API_demo / app.py 에 저장됩니다.

app.py 파일과 initialize_app 함수를 살펴보십시오.

def initialize_app(flask_app): 
	configure_app(flask_app) 

	blueprint = Blueprint('api', __name__, url_prefix='/api') 
	api.init_app(blueprint) 
	api.add_namespace(blog_posts_namespace) 
	api.add_namespace(blog_categories_namespace) 
	flask_app.register_blueprint(blueprint) 

	db.init_app(flask_app)

이 함수는 여러 가지 작업을 수행하지만 특히 /API URL 접두사 아래에 API 를 호스팅하는 Flask Blueprint 를 설정합니다. 이를 통해 애플리케이션의 API 부분을 다른 부분과 분리 할 수 있습니다. 앱의 프론트 엔드는 동일한 Flask 애플리케이션에서 호스팅되지만 다른 청사진 (/URL 접두사 포함)으로 호스팅 될 수 있습니다.

RESTPlus API 자체도 여러 개의 개별 네임 스페이스로 나뉩니다. 각 네임 스페이스에는 고유 한 URL 접두사가 있으며 /API/blog/endpoints 디렉토리의 별도 파일에 저장됩니다. 이러한 네임 스페이스를 API 에 추가하려면 api.add_namespace() 함수를 사용해야 합니다.

initialize_app 는 settings.py 에서 로드된 구성 값을 설정하고 Flask-SQLAlchemy 의 마법을 통해 데이터베이스를 사용하도록 앱을 구성합니다.

Defining API namespaces and RESTFul resources

API 네임 스페이스, RESTFul 리소스 및 HTTP 메소드를 사용하여 API 가 구성됩니다. 위에서 설명한 것처럼 네임 스페이스를 사용하면 API 정의를 여러 파일로 분할 할 수 있으며 각 파일은 다른 URL 접두어로 API 의 일부를 정의합니다.

RESTFul 리소스는 애플리케이션에서 사용하는 다양한 유형의 데이터에 해당하는 API 를 엔드 포인트로 구성하는 데 사용됩니다. 각 엔드 포인트는 다른 HTTP 메소드를 사용하여 호출됩니다. 각 메소드는 API 에 다른 명령을 발행합니다. 예를 들어, GET 은 API 에서 리소스를 가져 오는 데 사용되고 PUT 은 정보를 업데이트하는 데 사용되고 DELETE 는 삭제합니다.

  • GET /blog/categories/1 – Retrieve category with ID 1
  • PUT /blog/categories/1 – Update the category with ID 1
  • DELTE /blog/categories/1 – Delete the category with ID 1

자원에는 일반적으로 연관된 콜렉션 엔드 포인트가 있으며, 이는 새로운 자원 (POST) 또는 페치리스트 (GET)를 작성하는 데 사용할 수 있습니다.

  • GET /blog/categories – Retrieve a list of categories
  • POST /blog/categories – Create a new category

Flask-RESTPlus 를 사용하면 다음 코드 블록으로 위에 나열된 모든 엔드 포인트에 대한 API 를 정의 할 수 있습니다. 네임 스페이스를 만드는 것으로 시작하고, 컬렉션, 리소스 및 관련 HTTP 메서드를 만듭니다.

ns = api.namespace('blog/categories', description='Operations related to blog categories') 

@ns.route('/') 
class CategoryCollection(Resource): 
	
	def get(self): 
		"""Returns list of blog categories.""" 
		return get_all_categories() 

	@api.response(201, 'Category successfully created.') 
	def post(self): 
		"""Creates a new blog category.""" 
		create_category(request.json) 
		return None, 201 

@ns.route('/') 
@api.response(404, 'Category not found.') 
class CategoryItem(Resource): 

	def get(self, id): 
		"""Returns details of a category.""" 
		return get_category(id) 

	@api.response(204, 'Category successfully updated.') 
	def put(self, id): 
		"""Updates a blog category.""" 
		update_category(id, request.json) 
		return None, 204 

	@api.response(204, 'Category successfully deleted.') 
		def delete(self, id): 
		"""Deletes blog category.""" 
		delete_category(id) 
		return None, 204

api.namespace() 함수는 URL 접두사가 있는 새 네임 스페이스를 만듭니다. 설명 필드는 Swagger UI 에서이 메소드 세트를 설명하는 데 사용됩니다.

@ns.route() 데코레이터는 주어진 리소스와 연결될 URL 을 지정하는 데 사용됩니다. @ns.route('/')와 같이 꺾쇠 괄호를 사용하여 경로 매개 변수를 지정할 수 있습니다.

선택적으로 변환기 및 콜론의 이름을 사용하여 매개 변수 유형을 지정할 수 있습니다. 사용 가능한 변환기는 문자열: ( 기본값), 경로: ( 슬래시가있는 문자열), int:, float: 및 uuid:입니다.

URL 변환기는 Flask 의 기반이되는 Werkzeug 라이브러리에서 제공됩니다. Werkzeug 문서에서 자세한 내용을 읽을 수 있습니다. 불행히도 모든 Werkzeug 변환기 옵션이 현재 Flask-RESTPlus 에서 지원되는 것은 아닙니다. 플라스크의 url_map 옵션을 사용하여 추가 유형을 추가 할 수 있습니다.

각 자원은 HTTP 메소드에 맵핑 될 함수를 포함하는 클래스입니다. get, post, put, delete, patch, options 및 head 기능이 맵핑됩니다.

docstring 이 어떤 함수에도 존재하면 Swagger UI 에 "Implementation Notes"로 표시됩니다. 마크 다운 구문을 사용하여 이러한 메모의 서식을 지정할 수 있습니다.

@api.response() 데코레이터를 사용하여 각 메소드가 리턴 할 HTTP 상태 코드와 상태 코드의 의미를 나열 할 수 있습니다.

이 코드가 모두 배치되면 Swagger UI 에 메소드가 문서화됩니다.

restplusapimethoddetails.png

Swagger UI 문서에는 매개 변수를 설정할 수있는 양식도 포함되어 있습니다. 요청 본문이 예상되는 경우 해당 형식이 오른쪽에 지정됩니다.

당신이 그것을 시도하면 그것을 밖으로보십시오! 버튼을 클릭하면 요청이 API 로 전송되고 응답이 화면에 표시됩니다.

Documenting and validating method parameters

RESTFul 컬렉션에서 새 리소스를 업데이트하거나 만들려면 요청 본문에 항목 데이터를 JSON 으로 직렬화하여 보내야합니다. Flask-RESTPlus 를 사용하면 API 모델을 사용하여 수신 JSON 객체의 형식을 자동으로 문서화하고 확인할 수 있습니다.

RESTPlus API 모델은 모든 예상 필드를 나열하여 오브젝트의 형식을 정의합니다. 각 필드에는 연결된 유형 (예 : String, Integer, DateTime)이 있으며 이는 유효한 것으로 간주되는 값을 결정합니다.

데모 앱은 serializers.py 파일에 여러 가지 API 모델이 있습니다. 간단한 예는 다음과 같습니다.

from flask_restplus import fields 
from rest_api_demo.api.restplus import api 
blog_post = api.model('Blog post', { 
	'id': fields.Integer(description='The unique identifier of a blog post'), 
	'title': fields.String(required=True, description='Article title'), 
	'body': fields.String(required=True, description='Article content'), 
	'status': fields.String(required=True, enum=['DRAFT', 'PUBLISHED', 'DELETED']), 
	'pub_date': fields.DateTime, 
})

모델이 정의되면 @api.expect() 데코레이터를 사용하여 메소드에 첨부 할 수 있습니다.

@ns.route('/') 
class BlogPostCollection(Resource): 

	@api.response(201, 'Blog post successfully created.') 
	@api.expect(blog_post) 
	def post(self): 
		...

Field options

모든 필드는 동작을 변경할 수있는 몇 가지 일반적인 옵션을 공유합니다.

  • required – 필수 필드
  • default – 필드의 기본값
  • description – 필드 설명 (Swagger UI 에 표시됨)
  • example – 선택적 예 값 (Swagger UI 에 표시됨)

필드에 추가 검증 옵션을 추가하여보다 구체적으로 만들 수 있습니다.

String:

  • min_length and max_length – 문자열의 최소 및 최대 길이
  • pattern – String 과 일치해야 하는 정규식
'slug':  fields.String(required=True,  pattern='^[a-z0-9-]+$',  min_length=5,  max_length=200)

Numbers (IntegerFloatFixedArbitrary):

  • min and max – minimum and maximum values
  • exclusiveMin and exclusiveMax – as above, but the boundary values are not valid
  • multiple – number must be a multiple of this value

소스 코드를보고 RESTPlus 모델 필드에 대해 자세히 알아볼 수 있습니다.

Nested models and lists

API 모델의 필드는 다른 모델을 예상 값으로 사용할 수 있습니다. 그런 다음이 필드에 유효한 값으로 JSON 오브젝트를 제공하십시오.

'details':  fields.Nested(blog_post_details)

필드에는 값 목록 또는 중첩 된 개체 목록이 필요할 수도 있습니다.

'item_ids': fields.List(fields.Integer), 
'items': fields.List(fields.Nested(blog_post))

Model inheritance

두 개의 유사한 모델이있는 경우 모델 상속을 사용하여 추가 필드가있는 모델의 정의를 확장 할 수 있습니다. 아래 예에는 페이지 매김이라는 하나의 일반 API 모델이 있으며 api.inherit () 메서드를 사용하여보다 구체적인 모델 page_of_blog_posts 를 만듭니다.

pagination = api.model('A page of results', { 
	'page': fields.Integer(description='Number of this page of results'), 
	'pages': fields.Integer(description='Total number of pages of results'), 
	'per_page': fields.Integer(description='Number of items per page of results'), 
	'total': fields.Integer(description='Total number of results'), 
}) 

page_of_blog_posts = api.inherit('Page of blog posts', pagination, { 
	'items': fields.List(fields.Nested(blog_post)) 
})

Marshaling output JSON objects

API 모델은 시리얼 라이저로도 사용할 수 있습니다. @api.marshal_with(model)로 메소드를 장식하면 Flask-RESTPlus 는 모델에 지정된 것과 동일한 필드를 가진 JSON 객체를 생성합니다.

이 메소드는 필드와 이름이 같은 속성을 가진 객체를 반환하면 됩니다. 또는 이 메소드는 모델 필드 이름과 동일한 키에 값이 지정된 사전을 리턴 할 수 있습니다.

예를 들어, 메소드는 API 모델과 동일한 필드를 가진 SQLAlchemy ORM 오브젝트를 리턴 할 수 있습니다.

@ns.route('/') 
@api.response(404, 'Category not found.') 
class CategoryItem(Resource): 

	@api.marshal_with(category_with_posts) 
	def get(self, id): 
		""" 
		Returns a category with a list of posts. 
		""" 
		return Category.query.filter(Category.id == id).one()

객체 목록을 반환하려면 @api.marshal_list_with(model) 데코레이터를 사용하십시오.

attribute 키워드를 사용하면 필드 값을 가져 오는 객체 속성을 지정할 수 있습니다.

'firstName':  fields.String(attribute='first_name'),

속성 매개 변수를 사용하면 객체 구조에 더 깊이 중첩 된 값을 가져올 수 있습니다.

'firstName':  fields.String(attribute='user.first_name'),

더 복잡한 경우 람다 함수를 사용하여 값을 쿼리 할 수 있습니다.

'fullName':  fields.String(attribute=lambda  x:  '{} {}'.format(x.first_name,  x.last_name)),

Handling errors

API 엔드 포인트 함수를 작성할 때 이행 할 수없는 요청을 처리 할 수 있습니다. 이러한 경우 사용자에게 오류 메시지를 반환하는 것이 유일한 방법입니다. api.abort() 함수를 사용하면됩니다.

api.abort(code=400,  message="Sorry, Dave. I'm afraid I can't do that.")

명시적으로 오류를 직접 처리하지 않으면 Flask 는 예외를 잡아서 HTTP 500 오류 페이지로 바꿉니다.

@api.errorhandler 데코레이터를 사용하여 기본 오류 처리기를 재정의 할 수 있습니다.

@api.errorhandler 
def default_error_handler(e): 
	message = 'An unhandled exception occurred.' 
	log.exception(message) 

	if not settings.FLASK_DEBUG: 
		return {'message': message}, 500

다른 유형의 예외에 대해 사용자 정의 오류 처리 논리를 지정할 수 있습니다.

@api.errorhandler(NoResultFound) 
def database_not_found_error_handler(e): 
	log.warning(traceback.format_exc()) 
	return {'message': 'A database result was required but none was found.'}, 404

Flask 응용 프로그램이 DEBUG 모드에서 실행중인 경우 위에서 설명한 default_error_handler 함수는 응답을 반환하지 않습니다. 오류 메시지를 반환하는 대신 Werkzeug 대화식 디버거가 활성화됩니다.

werkzeuginteractivedebugger.png

Resetting the database

db.SQLite 파일을 삭제하거나 단순히 데이터베이스를 빈 상태로 재설정하려는 경우 Python 콘솔에서 다음 명령을 입력 할 수 있습니다.

>>> from rest_api_demo.app import initialize_app, app 
>>> from rest_api_demo.database import reset_database 
>>> 
>>> initialize_app(app) 
>>> with app.app_context(): 
... reset_database()

Further reading

인터넷에는 많은 플라스크 계몽을 안내 할 수있는 많은 자료가 있습니다. 다음 사항을 알아 두는 것이 좋습니다.

추천
0

댓글 0개

전체 121 |RSS
파이썬 내용 검색

회원로그인

진행중 포인트경매

  1. 참여3 회 시작24.04.25 20:23 종료24.05.02 20:23
(주)에스아이알소프트 / 대표:홍석명 / (06211) 서울특별시 강남구 역삼동 707-34 한신인터밸리24 서관 1404호 / E-Mail: admin@sir.kr
사업자등록번호: 217-81-36347 / 통신판매업신고번호:2014-서울강남-02098호 / 개인정보보호책임자:김민섭(minsup@sir.kr)
© SIRSOFT