테스트 사이트 - 개발 중인 베타 버전입니다

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

· 5년 전 · 2280

이 기사에서는 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

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

댓글 작성

댓글을 작성하시려면 로그인이 필요합니다.

로그인하기

게시글 목록

번호 제목
831
830
829
827
824
823
821
819
818
814
809
808
807
806
803
801
799
798
797
796
794
786
783
782
767
742
655
614
591
590