1656753984
В этом руководстве показано, как легко создать RESTful API с помощью Flask и APIFairy.
К концу этого урока вы сможете:
APIFairy — это платформа API, написанная Мигелем Гринбергом, которая позволяет легко создавать API с помощью Flask.
APIFairy предоставляет четыре ключевых компонента для простого создания API во Flask:
Давайте подробно рассмотрим каждый...
APIFairy предоставляет набор декораторов для определения входов, выходов и аутентификации для каждой конечной точки API:
APIFairy предоставляет пять основных декораторов:
Ввод (с помощью @body
декоратора) и вывод (с помощью @response
декоратора) конечной точки API определяются как схемы:
class EntrySchema(ma.Schema):
"""Schema defining the attributes in a journal entry."""
id = ma.Integer()
entry = ma.String()
user_id = ma.Integer()
Схемы используют зефир для определения типов данных как классов.
Декоратор @authenticate
используется для проверки заголовка аутентификации, предоставленного в URL-запросе к каждой конечной точке API. Схема аутентификации реализована с помощью Flask-HTTPAuth , который также был создан Мигелем Гринбергом.
Типичным подходом к аутентификации API будет определение базовой аутентификации для защиты маршрута для получения токена аутентификации:
basic_auth = HTTPBasicAuth()
@basic_auth.verify_password
def verify_password(email, password):
user = User.query.filter_by(email=email).first()
if user.is_password_correct(password):
return user
А также определить аутентификацию токена для защиты большинства маршрутов на основе чувствительного ко времени токена аутентификации:
token_auth = HTTPTokenAuth()
@token_auth.verify_token
def verify_token(auth_token):
return User.verify_auth_token(auth_token)
Одной из замечательных особенностей APIFairy является красивая документация по API, которая генерируется автоматически:
Документация создается на основе строк документации в исходном коде вместе со следующими переменными конфигурации:
APIFAIRY_TITLE
- название проектаAPIFAIRY_VERSION
- строка версии проектаAPIFAIRY_UI
- формат документации APIДля APIFAIRY_UI
, вы можете создавать шаблоны из одного из следующих средств визуализации документации OpenAPI:
Полный список доступных переменных конфигурации см. в документации по конфигурации .
В этом руководстве вы будете разрабатывать API журнала, позволяющий пользователям вести ежедневный журнал событий. Вы можете найти полный исходный код в репозитории flask-journal-api на GitLab.
Основные используемые пакеты Python:
Вы будете разрабатывать API постепенно:
Давайте перейдем к созданию API с помощью Flask и APIFairy...
Start by creating a new project folder and a virtual environment:
$ mkdir flask-journal-api
$ cd flask-journal-api
$ python3 -m venv venv
$ source venv/bin/activate
(venv)$
Feel free to swap out virtualenv and Pip for Poetry or Pipenv. For more, review Modern Python Environments.
Go ahead and add the following files and folders:
├── app.py
├── instance
│ └── .gitkeep
├── project
│ ├── __init__.py
│ └── journal_api
│ ├── __init__.py
│ └── routes.py
└── requirements.txt
Next, to install the necessary Python packages, add the dependencies to the requirements.txt file in the project root:
apifairy==0.9.1
Flask==2.1.2
Flask-SQLAlchemy==2.5.1
marshmallow-sqlalchemy==0.28.0
Install:
(venv)$ pip install -r requirements.txt
This Flask project will utilize two best practices for Flask applications:
Start by defining the Application Factory function in project/__init__.py:
from apifairy import APIFairy
from flask import Flask, json
from flask_marshmallow import Marshmallow
# -------------
# Configuration
# -------------
# Create the instances of the Flask extensions in the global scope,
# but without any arguments passed in. These instances are not
# attached to the Flask application at this point.
apifairy = APIFairy()
ma = Marshmallow()
# ----------------------------
# Application Factory Function
# ----------------------------
def create_app():
# Create the Flask application
app = Flask(__name__)
initialize_extensions(app)
register_blueprints(app)
return app
# ----------------
# Helper Functions
# ----------------
def initialize_extensions(app):
# Since the application instance is now created, pass it to each Flask
# extension instance to bind it to the Flask application instance (app)
apifairy.init_app(app)
ma.init_app(app)
def register_blueprints(app):
# Import the blueprints
from project.journal_api import journal_api_blueprint
# Since the application instance is now created, register each Blueprint
# with the Flask application instance (app)
app.register_blueprint(journal_api_blueprint, url_prefix='/journal')
После определения функции Application Factory ее можно вызвать в app.py в папке верхнего уровня проекта:
from project import create_app
# Call the application factory function to construct a Flask application
# instance using the development configuration
app = create_app()
Давайте определим journal_api
план. Начните с определения схемы journal_api
в project/journal_api/__init__.py :
"""
The 'journal_api' blueprint handles the API for managing journal entries.
Specifically, this blueprint allows for journal entries to be added, edited,
and deleted.
"""
from flask import Blueprint
journal_api_blueprint = Blueprint('journal_api', __name__, template_folder='templates')
from . import routes
Теперь пришло время определить конечные точки API для журнала в project/journal_api/routes.py .
Начните с необходимого импорта:
from apifairy import body, other_responses, response
from flask import abort
from project import ma
from . import journal_api_blueprint
Для этой начальной версии Flask Journal API база данных будет представлять собой список записей журнала:
# --------
# Database
# --------
messages = [
dict(id=1, entry='The sun was shining when I woke up this morning.'),
dict(id=2, entry='I tried a new fruit mixture in my oatmeal for breakfast.'),
dict(id=3, entry='Today I ate a great sandwich for lunch.')
]
Затем определите схемы для создания новой записи журнала и возврата записей журнала:
# -------
# Schemas
# -------
class NewEntrySchema(ma.Schema):
"""Schema defining the attributes when creating a new journal entry."""
entry = ma.String(required=True)
class EntrySchema(ma.Schema):
"""Schema defining the attributes in a journal entry."""
id = ma.Integer()
entry = ma.String()
new_entry_schema = NewEntrySchema()
entry_schema = EntrySchema()
entries_schema = EntrySchema(many=True)
Оба этих класса схемы наследуются от ma.Schema , предоставляемого Flask-Marshmallow. Также рекомендуется создавать объекты этих схем, так как это позволяет определить схему, которая может возвращать несколько записей (используя many=True
аргумент).
Теперь мы готовы определить конечные точки API!
Начните с извлечения всех записей журнала:
@journal_api_blueprint.route('/', methods=['GET'])
@response(entries_schema)
def journal():
"""Return all journal entries"""
return messages
Эта функция представления использует @response
декоратор, чтобы указать, что возвращаются несколько записей. Функция просмотра возвращает полный список записей журнала ( return messages
).
Затем создайте конечную точку API для добавления новой записи журнала:
@journal_api_blueprint.route('/', methods=['POST'])
@body(new_entry_schema)
@response(entry_schema, 201)
def add_journal_entry(kwargs):
"""Add a new journal entry"""
new_message = dict(**kwargs, id=messages[-1]['id']+1)
messages.append(new_message)
return new_message
Эта функция представления использует @body
декоратор для определения ввода в конечную точку API и @response
декоратор для определения выходных данных из конечной точки API.
Входные данные, полученные от @body
декоратора, передаются в add_journal_entry()
функцию представления в качестве аргумента ( kwargs
аргументы ключевого слова ) . Затем эти данные используются для создания новой записи журнала и добавления ее в базу данных:
new_message = dict(**kwargs, id=messages[-1]['id']+1)
messages.append(new_message)
Затем возвращается вновь созданная запись журнала ( return new_message
). Обратите внимание, как @response
декоратор определяет код возврата как 201 (Создано), чтобы указать, что запись журнала была добавлена в базу данных.
Создайте конечную точку API для получения определенной записи журнала:
@journal_api_blueprint.route('/<int:index>', methods=['GET'])
@response(entry_schema)
@other_responses({404: 'Entry not found'})
def get_journal_entry(index):
"""Return a journal entry"""
if index >= len(messages):
abort(404)
return messages[index]
Эта функция просмотра использует @other_responses
декоратор для указания нестандартных ответов.
Декоратор
@other_responses
используется только для целей документации! Он не предоставляет никакой функциональности с точки зрения возврата кодов ошибок.
Create the API endpoint for updating a journal entry:
@journal_api_blueprint.route('/<int:index>', methods=['PUT'])
@body(new_entry_schema)
@response(entry_schema)
@other_responses({404: 'Entry not found'})
def update_journal_entry(data, index):
"""Update a journal entry"""
if index >= len(messages):
abort(404)
messages[index] = dict(data, id=index+1)
return messages[index]
This view function uses the @body
and @response
decorators to define the inputs and outputs (respectively) for this API endpoint. Additionally, the @other_responses
decorator defines the non-standard response if the journal entry is not found.
Finally, create the API endpoint for deleting a journal entry:
@journal_api_blueprint.route('/<int:index>', methods=['DELETE'])
@other_responses({404: 'Entry not found'})
def delete_journal_entry(index):
"""Delete a journal entry"""
if index >= len(messages):
abort(404)
messages.pop(index)
return '', 204
This view function does not use the @body
and @response
decorators, as there are no inputs or outputs for this API endpoint. If the journal entry is successfully deleted, then a 204 (No Content) status code is returned with no data.
To test things out, within one terminal window, configure the Flask application and run the development server:
(venv) $ export FLASK_APP=app.py
(venv) $ export FLASK_ENV=development
(venv) $ flask run
Then, in a different terminal window, you can interact with the API. Feel free to use your tool of choice here, like cURL, HTTPie, Requests, or Postman.
Requests example:
$ python3
>>> import requests
>>>
>>> r = requests.get('http://127.0.0.1:5000/journal/')
>>> print(r.text)
>>>
>>> post_data = {'entry': "some message"}
>>> r = requests.post('http://127.0.0.1:5000/journal/', json=post_data)
>>> print(r.text)
Want to test out the API endpoints more easily? Check out this script, which adds CLI commands for interacting with the API endpoints for retrieving, creating, updating, and deleting journal entries.
An incredible feature of APIFairy is the automatic API documentation creation!
There are three key aspects to configuring the API documentation:
Мы уже рассмотрели первый пункт в предыдущем разделе, так как включили строки документации для каждой функции представления. Например, journal()
функция просмотра имеет краткое описание назначения этой конечной точки API:
@journal_api_blueprint.route('/', methods=['GET'])
@response(entries_schema)
def journal():
"""Return all journal entries"""
return messages
Затем нам нужно включить строку документации для описания всего проекта в самом верху файла project/__init__.py :
"""
Welcome to the documentation for the Flask Journal API!
## Introduction
The Flask Journal API is an API (Application Programming Interface) for creating a **daily journal** that documents events that happen each day.
## Key Functionality
The Flask Journal API has the following functionality:
1. Work with journal entries:
* Create a new journal entry
* Update a journal entry
* Delete a journal entry
* View all journal entries
2. <More to come!>
## Key Modules
This project is written using Python 3.10.1.
The project utilizes the following modules:
* **Flask**: micro-framework for web application development which includes the following dependencies:
* **click**: package for creating command-line interfaces (CLI)
* **itsdangerous**: cryptographically sign data
* **Jinja2**: templating engine
* **MarkupSafe**: escapes characters so text is safe to use in HTML and XML
* **Werkzeug**: set of utilities for creating a Python application that can talk to a WSGI server
* **APIFairy**: API framework for Flask which includes the following dependencies:
* **Flask-Marshmallow** - Flask extension for using Marshmallow (object serialization/deserialization library)
* **Flask-HTTPAuth** - Flask extension for HTTP authentication
* **apispec** - API specification generator that supports the OpenAPI specification
* **pytest**: framework for testing Python projects
"""
...
Эта строка документации используется для описания всего проекта, включая ключевые функции и ключевые пакеты Python, используемые в проекте.
Наконец, необходимо определить некоторые переменные конфигурации, чтобы указать внешний вид документации API. Обновите create_app()
функцию в project/__init__.py :
def create_app():
# Create the Flask application
app = Flask(__name__)
# Configure the API documentation
app.config['APIFAIRY_TITLE'] = 'Flask Journal API'
app.config['APIFAIRY_VERSION'] = '0.1'
app.config['APIFAIRY_UI'] = 'elements'
initialize_extensions(app)
register_blueprints(app)
return app
Готовы ознакомиться с документацией по проекту? Запустите сервер разработки Flask через flask run
, а затем перейдите по адресу http://127.0.0.1:5000/docs , чтобы просмотреть документацию по API, созданную APIFairy:
На левой панели есть список конечных точек API для journal_api
схемы. При нажатии на одну из конечных точек отображаются все сведения об этой конечной точке:
Что удивительно в этой документации API, так это возможность увидеть, как работают конечные точки API (при условии, что сервер разработки Flask работает). На правой панели документации введите индекс записи журнала и нажмите «Отправить запрос API». Затем отображается ответ API:
Эта интерактивная документация облегчает пользователям понимание API!
For demonstration purposes, a SQLite database will be used in this tutorial.
Since Flask-SQLAlchemy was already installed at the beginning of this tutorial, we need to configure it in the project/__init__.py file.
Start by creating a SQLAlchemy()
object in the 'Configuration' section:
...
from apifairy import APIFairy
from flask import Flask, json
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy # <-- NEW!!
# -------------
# Configuration
# -------------
# Create the instances of the Flask extensions in the global scope,
# but without any arguments passed in. These instances are not
# attached to the Flask application at this point.
apifairy = APIFairy()
ma = Marshmallow()
database = SQLAlchemy() # <-- NEW!!
...
Next, update the create_app()
function to specify the necessary configuration variables:
def create_app():
# Create the Flask application
app = Flask(__name__)
# Configure the API documentation
app.config['APIFAIRY_TITLE'] = 'Flask Journal API'
app.config['APIFAIRY_VERSION'] = '0.1'
app.config['APIFAIRY_UI'] = 'elements'
# NEW!
# Configure the SQLite database (intended for development only!)
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{os.path.join(os.getcwd(), 'instance', 'app.db')}"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
initialize_extensions(app)
register_blueprints(app)
return app
Add the import to the top:
import os
The SQLALCHEMY_DATABASE_URI
configuration variable is critical to identifying the location of the SQLite database. For this tutorial, the database is stored in instance/app.db.
Finally, update the initialize_extensions()
function to initialize the Flask-SQLAlchemy object:
def initialize_extensions(app):
# Since the application instance is now created, pass it to each Flask
# extension instance to bind it to the Flask application instance (app)
apifairy.init_app(app)
ma.init_app(app)
database.init_app(app) # <-- NEW!!
Want to learn more about how this Flask app is wired together? Check out my course on how to build, test, and deploy a Flask application:
Create a new project/models.py file to define the database table to represent the journal entries:
from project import database
class Entry(database.Model):
"""Class that represents a journal entry."""
__tablename__ = 'entries'
id = database.Column(database.Integer, primary_key=True)
entry = database.Column(database.String, nullable=False)
def __init__(self, entry: str):
self.entry = entry
def update(self, entry: str):
self.entry = entry
def __repr__(self):
return f'<Entry: {self.entry}>'
This new class, Entry
, specifies that the entries
database table will contain two elements (for now!) to represent a journal entry:
id
- the primary key (primary_key=True
) for the table, which means that it's a unique identifier for each element (row) in the tableentry
- string for storing the journal entry textWhile models.py defines the database table, it doesn't create the tables in the SQLite database. To create the tables, start the Flask shell in a terminal window:
(venv)$ flask shell
>>> from project import database
>>> database.drop_all()
>>> database.create_all()
>>> quit()
(venv)$
Since we're progressing on to use a SQLite database, start by deleting the temporary database
(Python list) that was defined in project/journal_api/routes.py:
# --------
# Database
# --------
messages = [
dict(id=1, entry='The sun was shining when I woke up this morning.'),
dict(id=2, entry='I tried a new fruit mixture in my oatmeal for breakfast.'),
dict(id=3, entry='Today I ate a great sandwich for lunch.')
]
Next, we need to update each API endpoint (i.e., the view functions) to utilize the SQLite database.
Start by updating the journal()
view function:
@journal_api_blueprint.route('/', methods=['GET'])
@response(entries_schema)
def journal():
"""Return all journal entries"""
return Entry.query.all()
The complete list of journal entries is now retrieved from the SQLite database. Notice how the schemas or decorators for this view function did not need to change... only the underlying process for getting the users changed!
Add the import:
from project.models import Entry
Next, update the add_journal_entry()
view function:
@journal_api_blueprint.route('/', methods=['POST'])
@body(new_entry_schema)
@response(entry_schema, 201)
def add_journal_entry(kwargs):
"""Add a new journal entry"""
new_message = Entry(**kwargs)
database.session.add(new_message)
database.session.commit()
return new_message
The inputs to this view function are specified by new_entry_schema
:
class NewEntrySchema(ma.Schema):
"""Schema defining the attributes when creating a new journal entry."""
entry = ma.String(required=True)
new_entry_schema = NewEntrySchema()
The entry
string is used to create a new instance of the Entry
class (defined in models.py) and this journal entry is then added to the database.
Add the import:
from project import database
Next, update get_journal_entry()
:
@journal_api_blueprint.route('/<int:index>', methods=['GET'])
@response(entry_schema)
@other_responses({404: 'Entry not found'})
def get_journal_entry(index):
"""Return a journal entry"""
entry = Entry.query.filter_by(id=index).first_or_404()
return entry
This function now attempts to look up the specified journal entry (based on the index
):
entry = Entry.query.filter_by(id=index).first_or_404()
If the entry exists, it's returned to the user. If the entry does not exist, a 404 (Not Found) error is returned.
Next, update update_journal_entry()
:
@journal_api_blueprint.route('/<int:index>', methods=['PUT'])
@body(new_entry_schema)
@response(entry_schema)
@other_responses({404: 'Entry not found'})
def update_journal_entry(data, index):
"""Update a journal entry"""
entry = Entry.query.filter_by(id=index).first_or_404()
entry.update(data['entry'])
database.session.add(entry)
database.session.commit()
return entry
The update_journal_entry()
view function now attempts to retrieve the specified journal entry:
entry = Entry.query.filter_by(id=index).first_or_404()
If the journal entry exists, the entry is updated with the new text and then saved to the database.
Finally, update delete_journal_entry()
:
@journal_api_blueprint.route('/<int:index>', methods=['DELETE'])
@other_responses({404: 'Entry not found'})
def delete_journal_entry(index):
"""Delete a journal entry"""
entry = Entry.query.filter_by(id=index).first_or_404()
database.session.delete(entry)
database.session.commit()
return '', 204
If the specified journal entry is found, then it's deleted from the database.
Run the development server. Test out each of the endpoints to ensure they still work.
Since this Flask project is an API, error codes should be returned in JSON format instead of the typical HTML format.
In the Flask project, this can be accomplished by using a custom error handler. In project/__init__.py, define a new function (register_error_handlers()
) at the bottom of the file:
def register_error_handlers(app):
@app.errorhandler(HTTPException)
def handle_http_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
# Start with the correct headers and status code from the error
response = e.get_response()
# Replace the body with JSON
response.data = json.dumps({
'code': e.code,
'name': e.name,
'description': e.description,
})
response.content_type = 'application/json'
return response
This function registers a new error handler for when an HTTPException
is raised to convert the output into JSON format.
Add the import:
from werkzeug.exceptions import HTTPException
Also, update the Application Factory function, create_app()
, to call this new function:
def create_app():
# Create the Flask application
app = Flask(__name__)
# Configure the API documentation
app.config['APIFAIRY_TITLE'] = 'Flask Journal API'
app.config['APIFAIRY_VERSION'] = '0.1'
app.config['APIFAIRY_UI'] = 'elements'
# Configure the SQLite database (intended for development only!)
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{os.path.join(os.getcwd(), 'instance', 'app.db')}"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
initialize_extensions(app)
register_blueprints(app)
register_error_handlers(app) # NEW!!
return app
Authentication is the process of validating the identity of a user attempting to access a system, which in this case is the API.
Authorization, on the other hand, is the process of verifying what specific resources a specific user should have access to.
APIFairy utilizes Flask-HTTPAuth for authentication support. In this tutorial, we'll be using Flask-HTTPAuth in two manners:
Аутентификация токена , используемая через Flask-HTTPAuth, часто называется аутентификацией носителя, поскольку процесс вызывает предоставление доступа к «носителю» токена. Маркер должен быть включен в заголовки HTTP в заголовке авторизации, например «Авторизация: носитель».
На следующей диаграмме показан типичный процесс взаимодействия нового пользователя с приложением для получения маркера аутентификации.
Поскольку Flask-HTTPAuth уже был установлен при установке APIFairy в начале этого руководства, нам просто нужно настроить его в файле project/__init__.py .
Начните с создания отдельных объектов для базовой и токеновой аутентификации:
...
import os
from apifairy import APIFairy
from flask import Flask, json
from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth # NEW!!
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from werkzeug.exceptions import HTTPException
# -------------
# Configuration
# -------------
# Create the instances of the Flask extensions in the global scope,
# but without any arguments passed in. These instances are not
# attached to the Flask application at this point.
apifairy = APIFairy()
ma = Marshmallow()
database = SQLAlchemy()
basic_auth = HTTPBasicAuth() # NEW!!
token_auth = HTTPTokenAuth() # NEW!!
...
Никаких дальнейших обновлений в project/__init__.py не требуется .
В project/models.py необходимо создать новую User
модель для представления пользователя:
class User(database.Model):
__tablename__ = 'users'
id = database.Column(database.Integer, primary_key=True)
email = database.Column(database.String, unique=True, nullable=False)
password_hashed = database.Column(database.String(128), nullable=False)
entries = database.relationship('Entry', backref='user', lazy='dynamic')
auth_token = database.Column(database.String(64), index=True)
auth_token_expiration = database.Column(database.DateTime)
def __init__(self, email: str, password_plaintext: str):
"""Create a new User object."""
self.email = email
self.password_hashed = self._generate_password_hash(password_plaintext)
def is_password_correct(self, password_plaintext: str):
return check_password_hash(self.password_hashed, password_plaintext)
def set_password(self, password_plaintext: str):
self.password_hashed = self._generate_password_hash(password_plaintext)
@staticmethod
def _generate_password_hash(password_plaintext):
return generate_password_hash(password_plaintext)
def generate_auth_token(self):
self.auth_token = secrets.token_urlsafe()
self.auth_token_expiration = datetime.utcnow() + timedelta(minutes=60)
return self.auth_token
@staticmethod
def verify_auth_token(auth_token):
user = User.query.filter_by(auth_token=auth_token).first()
if user and user.auth_token_expiration > datetime.utcnow():
return user
def revoke_auth_token(self):
self.auth_token_expiration = datetime.utcnow()
def __repr__(self):
return f'<User: {self.email}>'
Добавьте импорт:
import secrets
from datetime import datetime, timedelta
from werkzeug.security import check_password_hash, generate_password_hash
Модель User
использует werkzeug.security
для хэширования пароль пользователя перед его сохранением в базе данных.
Помните: никогда не храните открытый пароль в базе данных!
Модель User
использует secrets
для создания токена аутентификации для конкретного пользователя. Этот токен создается в generate_auth_token()
методе и включает дату/время истечения срока действия 60 минут в будущем:
def generate_auth_token(self):
self.auth_token = secrets.token_urlsafe()
self.auth_token_expiration = datetime.utcnow() + timedelta(minutes=60)
return self.auth_token
Существует статический метод, verify_auth_token()
который используется для проверки токена аутентификации (с учетом времени истечения срока действия) и возврата пользователя из действительного токена:
@staticmethod
def verify_auth_token(auth_token):
user = User.query.filter_by(auth_token=auth_token).first()
if user and user.auth_token_expiration > datetime.utcnow():
return user
Еще один интересный метод — revoke_auth_token()
, который используется для отзыва токена аутентификации для конкретного пользователя:
def revoke_auth_token(self):
self.auth_token_expiration = datetime.utcnow()
Чтобы установить отношение «один ко многим» между пользователем («один») и его записями («многие»), Entry
необходимо обновить модель, чтобы связать таблицы entries
и вместе:users
class Entry(database.Model):
"""Class that represents a journal entry."""
__tablename__ = 'entries'
id = database.Column(database.Integer, primary_key=True)
entry = database.Column(database.String, nullable=False)
user_id = database.Column(database.Integer, database.ForeignKey('users.id')) # <-- NEW!!
def __init__(self, entry: str):
self.entry = entry
def update(self, entry: str):
self.entry = entry
def __repr__(self):
return f'<Entry: {self.entry}>'
Модель User
уже содержит обратную ссылку на entries
таблицу:
entries = database.relationship('Entry', backref='user', lazy='dynamic')
Функциональность управления пользователями проекта Flask будет определена в отдельном проекте Blueprint под названием users_api_blueprint
.
Начните с создания нового каталога в «проекте» с именем «users_api». В этом каталоге создайте файл __init__.py :
from flask import Blueprint
users_api_blueprint = Blueprint('users_api', __name__)
from . import authentication, routes
Этот новый Blueprint необходимо зарегистрировать в Flask app
в файле Projects/__init__.py внутри register_blueprints()
функции:
def register_blueprints(app):
# Import the blueprints
from project.journal_api import journal_api_blueprint
from project.users_api import users_api_blueprint # NEW!!
# Since the application instance is now created, register each Blueprint
# with the Flask application instance (app)
app.register_blueprint(journal_api_blueprint, url_prefix='/journal')
app.register_blueprint(users_api_blueprint, url_prefix='/users') # NEW!!
Чтобы использовать Flask-HTTPAuth, необходимо определить несколько функций для проверки учетных данных пользователя.
Создайте новый файл project/users_api/authentication.py для обработки базовой проверки подлинности и проверки подлинности токена.
Для базовой аутентификации (проверка электронной почты и пароля пользователя):
from werkzeug.exceptions import Forbidden, Unauthorized
from project import basic_auth, token_auth
from project.models import User
@basic_auth.verify_password
def verify_password(email, password):
user = User.query.filter_by(email=email).first()
if user is None:
return None
if user.is_password_correct(password):
return user
@basic_auth.error_handler
def basic_auth_error(status=401):
error = (Forbidden if status == 403 else Unauthorized)()
return {
'code': error.code,
'message': error.name,
'description': error.description,
}, error.code, {'WWW-Authenticate': 'Form'}
Функция verify_password()
используется для проверки существования пользователя и правильности его пароля. Эта функция будет использоваться Flask-HTTPAuth для проверки пароля, когда требуется базовая аутентификация (спасибо @basic_auth.verify_password
декоратору).
Кроме того, для базовой проверки подлинности определен обработчик ошибок, который возвращает информацию об ошибке в формате JSON.
Для аутентификации токена (обработка токена, чтобы определить, действителен ли пользователь):
@token_auth.verify_token
def verify_token(auth_token):
return User.verify_auth_token(auth_token)
@token_auth.error_handler
def token_auth_error(status=401):
error = (Forbidden if status == 403 else Unauthorized)()
return {
'code': error.code,
'message': error.name,
'description': error.description,
}, error.code
Функция verify_token()
используется для проверки правильности токена аутентификации. Эта функция будет использоваться Flask-HTTPAuth для проверки токена, когда требуется аутентификация токена (спасибо @token_auth.verify_token
декоратору).
Кроме того, для проверки подлинности токена определен обработчик ошибок, который возвращает информацию об ошибке в формате JSON.
В users_api_blueprint
, будет два маршрута:
Для начала необходимо определить новый набор схем (с использованием зефира) в файле project/users_api/routes.py :
from project import ma
from . import users_api_blueprint
# -------
# Schemas
# -------
class NewUserSchema(ma.Schema):
"""Schema defining the attributes when creating a new user."""
email = ma.String()
password_plaintext = ma.String()
class UserSchema(ma.Schema):
"""Schema defining the attributes of a user."""
id = ma.Integer()
email = ma.String()
class TokenSchema(ma.Schema):
"""Schema defining the attributes of a token."""
token = ma.String()
new_user_schema = NewUserSchema()
user_schema = UserSchema()
token_schema = TokenSchema()
Эти схемы будут использоваться для определения входных и выходных данных для функций просмотра, определенных в этом файле.
Затем определите функцию представления для регистрации нового пользователя:
@users_api_blueprint.route('/', methods=['POST'])
@body(new_user_schema)
@response(user_schema, 201)
def register(kwargs):
"""Create a new user"""
new_user = User(**kwargs)
database.session.add(new_user)
database.session.commit()
return new_user
Добавьте импорт:
from apifairy import authenticate, body, other_responses, response
from project import basic_auth, database, ma
from project.models import User
Эта конечная точка API использует , new_user_schema
чтобы указать, что адрес электронной почты и пароль являются входными данными.
ПРИМЕЧАНИЕ. Поскольку электронная почта и пароль отправляются на эту конечную точку API, самое время вспомнить, что использование HTTP допустимо во время тестирования разработки, но HTTPS (безопасный) всегда следует использовать в рабочей среде.
Электронная почта и пароль (определенные как kwargs
аргументы ключевого слова) затем распаковываются для создания нового User
объекта, который сохраняется в базе данных:
new_user = User(**kwargs)
database.session.add(new_user)
database.session.commit()
Выходные данные конечной точки API определяются user_schema
идентификатором и адресом электронной почты нового пользователя.
Другая функция представления, которую необходимо определить в файле project/users_api/routes.py , предназначена для получения токена аутентификации:
@users_api_blueprint.route('/get-auth-token', methods=['POST'])
@authenticate(basic_auth)
@response(token_schema)
@other_responses({401: 'Invalid username or password'})
def get_auth_token():
"""Get authentication token"""
user = basic_auth.current_user()
token = user.generate_auth_token()
database.session.add(user)
database.session.commit()
return dict(token=token)
Декоратор @authenticate
используется в этом руководстве впервые и указывает, что для защиты этого маршрута следует использовать базовую аутентификацию:
@authenticate(basic_auth)
Когда пользователь хочет получить свой токен аутентификации, ему необходимо отправить запрос POST на эту конечную точку API с адресом электронной почты и паролем, встроенными в заголовок «Авторизация». Например, следующая команда Python с использованием пакета Requests может быть выполнена для этой конечной точки API:
>>> import requests
>>> r = requests.post(
'http://127.0.0.1:5000/users/get-auth-token',
auth=('pkennedy@hey.com', 'FlaskIsAwesome123')
)
Если базовая аутентификация прошла успешно, функция просмотра извлекает текущего пользователя, используя current_user()
метод, предоставляемый Flask-HTTPAuth:
user = basic_auth.current_user()
Для этого пользователя создается новый токен аутентификации:
token = user.generate_auth_token()
И этот токен сохраняется в базе данных, чтобы его можно было использовать для аутентификации пользователя в будущем (по крайней мере, в течение следующих 60 минут!).
Наконец, новый токен аутентификации возвращается пользователю для сохранения для всех последующих вызовов API.
При наличии процесса аутентификации пришло время добавить некоторые средства защиты к существующим конечным точкам API, чтобы убедиться, что только действительные пользователи могут получить доступ к приложению.
Эти обновления предназначены для функций представления, определенных в файле Projects/journal_api/routes.py .
Во- первых, обновите, journal()
чтобы возвращались только записи журнала для текущего пользователя:
@journal_api_blueprint.route('/', methods=['GET'])
@authenticate(token_auth)
@response(entries_schema)
def journal():
"""Return journal entries"""
user = token_auth.current_user()
return Entry.query.filter_by(user_id=user.id).all()
Обновите импорт вверху следующим образом:
from apifairy import authenticate, body, other_responses, response
from flask import abort
from project import database, ma, token_auth
from project.models import Entry
from . import journal_api_blueprint
@authenticate
Декоратор указывает, что при доступе к этой конечной точке API необходимо использовать аутентификацию с помощью токена . В качестве примера можно сделать следующий запрос GET с использованием запросов ( после получения токена аутентификации ):
>>> import requests
>>> headers = {'Authorization': f'Bearer {auth_token}'}
>>> r = requests.get('http://127.0.0.1:5000/journal/', headers=headers)
После аутентификации пользователя полный список записей журнала извлекается из базы данных на основе идентификатора пользователя:
user = token_auth.current_user()
return Entry.query.filter_by(user_id=user.id).all()
Выходные данные этой конечной точки API определяются @response
декоратором, который представляет собой список записей журнала (идентификатор, запись, идентификатор пользователя).
Далее обновите add_journal_entry()
:
@journal_api_blueprint.route('/', methods=['POST'])
@authenticate(token_auth)
@body(new_entry_schema)
@response(entry_schema, 201)
def add_journal_entry(kwargs):
"""Add a new journal entry"""
user = token_auth.current_user()
new_message = Entry(user_id=user.id, **kwargs)
database.session.add(new_message)
database.session.commit()
return new_message
Как и в предыдущей функции представления, @authenticate
декоратор используется для указания того, что при доступе к этой конечной точке API необходимо использовать аутентификацию с помощью токена. Кроме того, запись журнала теперь добавляется путем указания идентификатора пользователя, который должен быть связан с записью журнала:
user = token_auth.current_user()
new_message = Entry(user_id=user.id, **kwargs)
Новая запись журнала сохраняется в базе данных, и запись журнала возвращается (как определено @response
декоратором).
Далее обновите get_journal_entry()
:
@journal_api_blueprint.route('/<int:index>', methods=['GET'])
@authenticate(token_auth)
@response(entry_schema)
@other_responses({403: 'Forbidden', 404: 'Entry not found'})
def get_journal_entry(index):
"""Return a journal entry"""
user = token_auth.current_user()
entry = Entry.query.filter_by(id=index).first_or_404()
if entry.user_id != user.id:
abort(403)
return entry
Добавляется @authenticate
декоратор, чтобы указать, что для доступа к этой конечной точке API необходима проверка подлинности токена.
При попытке получить запись журнала добавляется дополнительная проверка, чтобы убедиться, что пользователь, пытающийся получить доступ к записи журнала, является фактическим «владельцем» записи. Если нет, то через abort()
функцию из Flask возвращается код ошибки 403 (Forbidden):
if entry.user_id != user.id:
abort(403)
Обратите внимание, что эта конечная точка API имеет два нестандартных ответа, указанных @other_responses
декоратором:
@other_responses({403: 'Forbidden', 404: 'Entry not found'})
Напоминание:
@other_responses
декоратор предназначен только для документации; функция просмотра несет ответственность за возникновение этих ошибок.
Далее обновите update_journal_entry()
:
@journal_api_blueprint.route('/<int:index>', methods=['PUT'])
@authenticate(token_auth)
@body(new_entry_schema)
@response(entry_schema)
@other_responses({403: 'Forbidden', 404: 'Entry not found'})
def update_journal_entry(data, index):
"""Update a journal entry"""
user = token_auth.current_user()
entry = Entry.query.filter_by(id=index).first_or_404()
if entry.user_id != user.id:
abort(403)
entry.update(data['entry'])
database.session.add(entry)
database.session.commit()
return entry
Обновления этой функции просмотра аналогичны другим функциям просмотра в этом разделе:
@authenticate
декоратор указывает, что для доступа к этой конечной точке API необходима аутентификация токена.Наконец, обновите delete_journal_entry()
:
@journal_api_blueprint.route('/<int:index>', methods=['DELETE'])
@authenticate(token_auth)
@other_responses({403: 'Forbidden', 404: 'Entry not found'})
def delete_journal_entry(index):
"""Delete a journal entry"""
user = token_auth.current_user()
entry = Entry.query.filter_by(id=index).first_or_404()
if entry.user_id != user.id:
abort(403)
database.session.delete(entry)
database.session.commit()
return '', 204
В этом руководстве показано, как легко и быстро создать API во Flask с помощью APIFairy.
Декораторы — это ключ к определению конечных точек API:
@arguments
- входные аргументы из строки запроса URL@body
- структура JSON-запроса@response
- структура ответа JSON@authenticate
- подход к аутентификации с использованием Flask-HTTPAuth@other_responses
- нестандартные ответы, такие как коды ошибок HTTPКроме того, документация API, созданная APIFairy, превосходна и предоставляет ключевую информацию для пользователей приложения.
Источник: https://testdriven.io
1594289280
The REST acronym is defined as a “REpresentational State Transfer” and is designed to take advantage of existing HTTP protocols when used for Web APIs. It is very flexible in that it is not tied to resources or methods and has the ability to handle different calls and data formats. Because REST API is not constrained to an XML format like SOAP, it can return multiple other formats depending on what is needed. If a service adheres to this style, it is considered a “RESTful” application. REST allows components to access and manage functions within another application.
REST was initially defined in a dissertation by Roy Fielding’s twenty years ago. He proposed these standards as an alternative to SOAP (The Simple Object Access Protocol is a simple standard for accessing objects and exchanging structured messages within a distributed computing environment). REST (or RESTful) defines the general rules used to regulate the interactions between web apps utilizing the HTTP protocol for CRUD (create, retrieve, update, delete) operations.
An API (or Application Programming Interface) provides a method of interaction between two systems.
A RESTful API (or application program interface) uses HTTP requests to GET, PUT, POST, and DELETE data following the REST standards. This allows two pieces of software to communicate with each other. In essence, REST API is a set of remote calls using standard methods to return data in a specific format.
The systems that interact in this manner can be very different. Each app may use a unique programming language, operating system, database, etc. So, how do we create a system that can easily communicate and understand other apps?? This is where the Rest API is used as an interaction system.
When using a RESTful API, we should determine in advance what resources we want to expose to the outside world. Typically, the RESTful API service is implemented, keeping the following ideas in mind:
The features of the REST API design style state:
For REST to fit this model, we must adhere to the following rules:
#tutorials #api #application #application programming interface #crud #http #json #programming #protocols #representational state transfer #rest #rest api #rest api graphql #rest api json #rest api xml #restful #soap #xml #yaml
1604399880
I’ve been working with Restful APIs for some time now and one thing that I love to do is to talk about APIs.
So, today I will show you how to build an API using the API-First approach and Design First with OpenAPI Specification.
First thing first, if you don’t know what’s an API-First approach means, it would be nice you stop reading this and check the blog post that I wrote to the Farfetchs blog where I explain everything that you need to know to start an API using API-First.
Before you get your hands dirty, let’s prepare the ground and understand the use case that will be developed.
If you desire to reproduce the examples that will be shown here, you will need some of those items below.
To keep easy to understand, let’s use the Todo List App, it is a very common concept beyond the software development community.
#api #rest-api #openai #api-first-development #api-design #apis #restful-apis #restful-api
1652251528
Opencart REST API extensions - V3.x | Rest API Integration : OpenCart APIs is fully integrated with the OpenCart REST API. This is interact with your OpenCart site by sending and receiving data as JSON (JavaScript Object Notation) objects. Using the OpenCart REST API you can register the customers and purchasing the products and it provides data access to the content of OpenCart users like which is publicly accessible via the REST API. This APIs also provide the E-commerce Mobile Apps.
Opencart REST API
OCRESTAPI Module allows the customer purchasing product from the website it just like E-commerce APIs its also available mobile version APIs.
Opencart Rest APIs List
Customer Registration GET APIs.
Customer Registration POST APIs.
Customer Login GET APIs.
Customer Login POST APIs.
Checkout Confirm GET APIs.
Checkout Confirm POST APIs.
If you want to know Opencart REST API Any information, you can contact us at -
Skype: jks0586,
Email: letscmsdev@gmail.com,
Website: www.letscms.com, www.mlmtrees.com
Call/WhatsApp/WeChat: +91–9717478599.
Download : https://www.opencart.com/index.php?route=marketplace/extension/info&extension_id=43174&filter_search=ocrest%20api
View Documentation : https://www.letscms.com/documents/api/opencart-rest-api.html
More Information : https://www.letscms.com/blog/Rest-API-Opencart
VEDIO : https://vimeo.com/682154292
#opencart_api_for_android #Opencart_rest_admin_api #opencart_rest_api #Rest_API_Integration #oc_rest_api #rest_api_ecommerce #rest_api_mobile #rest_api_opencart #rest_api_github #rest_api_documentation #opencart_rest_admin_api #rest_api_for_opencart_mobile_app #opencart_shopping_cart_rest_api #opencart_json_api
1652251629
Unilevel MLM Wordpress Rest API FrontEnd | UMW Rest API Woocommerce Price USA, Philippines : Our API’s handle the Unilevel MLM woo-commerce end user all functionalities like customer login/register. You can request any type of information which is listed below, our API will provide you managed results for your all frontend needs, which will be useful for your applications like Mobile App etc.
Business to Customer REST API for Unilevel MLM Woo-Commerce will empower your Woo-commerce site with the most powerful Unilevel MLM Woo-Commerce REST API, you will be able to get and send data to your marketplace from other mobile apps or websites using HTTP Rest API request.
Our plugin is used JWT authentication for the authorization process.
REST API Unilevel MLM Woo-commerce plugin contains following APIs.
User Login Rest API
User Register Rest API
User Join Rest API
Get User info Rest API
Get Affiliate URL Rest API
Get Downlines list Rest API
Get Bank Details Rest API
Save Bank Details Rest API
Get Genealogy JSON Rest API
Get Total Earning Rest API
Get Current Balance Rest API
Get Payout Details Rest API
Get Payout List Rest API
Get Commissions List Rest API
Withdrawal Request Rest API
Get Withdrawal List Rest API
If you want to know more information and any queries regarding Unilevel MLM Rest API Woocommerce WordPress Plugin, you can contact our experts through
Skype: jks0586,
Mail: letscmsdev@gmail.com,
Website: www.letscms.com, www.mlmtrees.com,
Call/WhatsApp/WeChat: +91-9717478599.
more information : https://www.mlmtrees.com/product/unilevel-mlm-woocommerce-rest-api-addon
Visit Documentation : https://letscms.com/documents/umw_apis/umw-apis-addon-documentation.html
#Unilevel_MLM_WooCommerce_Rest_API's_Addon #umw_mlm_rest_api #rest_api_woocommerce_unilevel #rest_api_in_woocommerce #rest_api_woocommerce #rest_api_woocommerce_documentation #rest_api_woocommerce_php #api_rest_de_woocommerce #woocommerce_rest_api_in_android #woocommerce_rest_api_in_wordpress #Rest_API_Woocommerce_unilevel_mlm #wp_rest_api_woocommerce
1596509565
In this tutorial I will show you the fundamentals of designing a RESTful API specification by applying REST principles and best practices, then you’ll be ready to try my online tutorial: How to design a REST API with API Designer?
If you already know what is meant by API in the context of RESTful web services, you can skip to the next section. If not, read on.
The abbreviation API stands for Application Programming Interface this in itself, does not help us understand what it is, however in the context of web services, it can refer to one of two things:
In this post, I will use the first understanding of this term. Even though both are correct, the most technically relevant for this post is the first: an API is a contract for how software applications talk to each other.
The acronym REST stands for REpresentational State Transfer. It is an architectural style used to represent the transmission of data from one application component to another. In the context of web services, we are talking about the representation of resources (i.e. data) transferred over HTTP by calling a URI that represents the data and via an HTTP method that represents the action to perform against the given data.
RESTful API design is the activity of describing the behavior of a web service in terms of its data structures and the actions you allow other application components to perform on its data by the principles of REST. Those principles are covered later in this blog.
Imagine that you are an Architect (the kind the design building) and you set out to build an office block without a blueprint. You turn up on the first day with a truck full of bricks and some cement. What are the chances that you’ll be successful and build a structure that conforms to code and more importantly, doesn’t fall? It’s about zero. Without a blueprint the chance of failure is high.
The same approach applies to web service development. You need a blueprint, or more appropriately, an API specification. This is necessary to evaluate the API design and solicit feedback before even starting to build the implementation.
In addition to providing a specification for the web service’s development, an API contract serves to document its expected behavior, data types, and security requirements.
You should now be satisfied that API design is necessary for a RESTful web service, and should start to wonder how is the best approach to actually designing an API specification.
The tooling chosen by an API designer has substantial influence over the designer’s productivity. Highly productive tools such as the Anypoint API Designer from MuleSoft is perfect for designing APIs with OAS (swagger) or RAML.
#integration #api #rest #rest api #restful #api design #raml #rest api design