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 /를여십시오.
다음과 유사한 페이지가 표시됩니다.
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 1PUT /blog/categories/1
– Update the category with ID 1DELTE /blog/categories/1
– Delete the category with ID 1
자원에는 일반적으로 연관된 콜렉션 엔드 포인트가 있으며, 이는 새로운 자원 (POST) 또는 페치리스트 (GET)를 작성하는 데 사용할 수 있습니다.
GET /blog/categories
– Retrieve a list of categoriesPOST /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 에 메소드가 문서화됩니다.
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
andmax_length
– 문자열의 최소 및 최대 길이pattern
– String 과 일치해야 하는 정규식
'slug': fields.String(required=True, pattern='^[a-z0-9-]+$', min_length=5, max_length=200)
Numbers (Integer
, Float
, Fixed
, Arbitrary
):
min
andmax
– minimum and maximum valuesexclusiveMin
andexclusiveMax
– as above, but the boundary values are not validmultiple
– 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 대화식 디버거가 활성화됩니다.
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
인터넷에는 많은 플라스크 계몽을 안내 할 수있는 많은 자료가 있습니다. 다음 사항을 알아 두는 것이 좋습니다.
- Flask docs
- Flask-RESTPlus docs
- Flask-SQLAlchemy docs and SQLAlchemy Tutorial – for more information about using a database with Flask
- Flask Extensions Registry – for more useful Flask extensions
- Rest API Design Rulebook
0
댓글 0개