1656731711
Este tutorial demonstra como criar facilmente uma API RESTful com Flask e APIFairy.
Ao final deste tutorial, você será capaz de:
APIFairy é um framework de API escrito por Miguel Grinberg que permite criar facilmente uma API com Flask.
APIFairy fornece quatro componentes principais para criar facilmente uma API no Flask:
Vamos explorar cada um em detalhes...
APIFairy fornece um conjunto de decoradores para definir as entradas, saídas e autenticação para cada endpoint da API:
APIFairy fornece cinco decoradores principais:
A entrada (usando o @body
decorador) e a saída (usando o @response
decorador) de um endpoint de API são definidas como esquemas:
class EntrySchema(ma.Schema):
"""Schema defining the attributes in a journal entry."""
id = ma.Integer()
entry = ma.String()
user_id = ma.Integer()
Os esquemas utilizam marshmallow para definir os tipos de dados como classes.
O @authenticate
decorador é usado para verificar o cabeçalho de autenticação fornecido na solicitação de URL para cada endpoint da API. O esquema de autenticação é implementado usando Flask-HTTPAuth , que também foi criado por Miguel Grinberg.
Uma abordagem típica de autenticação de API seria definir a autenticação básica para proteger a rota para recuperar um token de autenticação:
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
E também para definir a autenticação de token para proteger a maioria das rotas com base em um token de autenticação sensível ao tempo:
token_auth = HTTPTokenAuth()
@token_auth.verify_token
def verify_token(auth_token):
return User.verify_auth_token(auth_token)
Um dos grandes recursos do APIFairy é a bela documentação da API que é gerada automaticamente:
A documentação é gerada com base em docstrings no código-fonte junto com as seguintes variáveis de configuração:
APIFAIRY_TITLE
- nome do projetoAPIFAIRY_VERSION
- string de versão do projetoAPIFAIRY_UI
- formato da documentação da APIPara APIFAIRY_UI
, você pode gerar modelos de um dos seguintes renderizadores de documentação OpenAPI:
Para obter uma lista completa de variáveis de configuração disponíveis, consulte os documentos de configuração .
Você desenvolverá uma API de diário neste tutorial, permitindo que os usuários mantenham um diário de eventos. Você pode encontrar o código-fonte completo no repositório flask-journal-api no GitLab.
Principais pacotes Python usados:
Você desenvolverá a API de forma incremental:
Vamos pular para a criação de uma API usando Flask e APIFairy...
Comece criando uma nova pasta de projeto e um ambiente virtual:
$ mkdir flask-journal-api
$ cd flask-journal-api
$ python3 -m venv venv
$ source venv/bin/activate
(venv)$
Sinta-se à vontade para trocar virtualenv e Pip por Poetry ou Pipenv . Para saber mais, revise Ambientes Python Modernos .
Vá em frente e adicione os seguintes arquivos e pastas:
├── app.py
├── instance
│ └── .gitkeep
├── project
│ ├── __init__.py
│ └── journal_api
│ ├── __init__.py
│ └── routes.py
└── requirements.txt
Em seguida, para instalar os pacotes Python necessários, adicione as dependências ao arquivo requirements.txt na raiz do projeto:
apifairy==0.9.1
Flask==2.1.2
Flask-SQLAlchemy==2.5.1
marshmallow-sqlalchemy==0.28.0
Instalar:
(venv)$ pip install -r requirements.txt
Este projeto Flask utilizará duas práticas recomendadas para aplicativos Flask:
Comece definindo a função Application Factory em 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')
Com a função Application Factory definida, ela pode ser chamada em app.py na pasta de nível superior do projeto:
from project import create_app
# Call the application factory function to construct a Flask application
# instance using the development configuration
app = create_app()
Vamos definir o journal_api
plano. Comece definindo o journal_api
blueprint em 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
Agora é hora de definir os endpoints da API para o diário em project/journal_api/routes.py .
Comece com as importações necessárias:
from apifairy import body, other_responses, response
from flask import abort
from project import ma
from . import journal_api_blueprint
Para esta versão inicial da API do Flask Journal, o banco de dados será uma lista de entradas de diário:
# --------
# 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.')
]
Em seguida, defina os esquemas para criar uma nova entrada de diário e para retornar as entradas de diário:
# -------
# 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)
Ambas as classes de esquema herdam de ma.Schema , que é fornecido pelo Flask-Marshmallow. Também é uma boa ideia criar objetos desses esquemas, pois isso permite definir um esquema que pode retornar várias entradas (usando o many=True
argumento).
Agora estamos prontos para definir os endpoints da API!
Comece recuperando todas as entradas de diário:
@journal_api_blueprint.route('/', methods=['GET'])
@response(entries_schema)
def journal():
"""Return all journal entries"""
return messages
Essa função de exibição usa o @response
decorador para definir que várias entradas sejam retornadas. A função de visualização retorna a lista completa de entradas de diário ( return messages
).
Em seguida, crie o endpoint da API para adicionar uma nova entrada de diário:
@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
Essa função de visualização usa o @body
decorador para definir a entrada para o endpoint da API e o @response
decorador para definir a saída do endpoint da API.
Os dados de entrada que são analisados do @body
decorador são passados para a função add_journal_entry()
de visualização como o argumento kwargs
( argumentos da palavra - chave ). Esses dados são então usados para criar uma nova entrada de diário e adicioná-la ao banco de dados:
new_message = dict(**kwargs, id=messages[-1]['id']+1)
messages.append(new_message)
A entrada de diário recém-criada é então retornada ( return new_message
). Observe como o @response
decorador define o código de retorno como 201 (Criado) para indicar que a entrada de diário foi adicionada ao banco de dados.
Crie o endpoint da API para recuperar uma entrada de diário específica:
@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]
Esta função de visualização usa o @other_responses
decorador para especificar respostas não padrão.
O
@other_responses
decorador é usado apenas para fins de documentação! Ele não fornece nenhuma funcionalidade em termos de retorno de códigos de erro.
Crie o endpoint da API para atualizar uma entrada de diário:
@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]
Essa função de visualização usa os decoradores @body
e @response
para definir as entradas e saídas (respectivamente) para este endpoint da API. Além disso, o @other_responses
decorador define a resposta não padrão se a entrada de diário não for encontrada.
Por fim, crie o endpoint da API para excluir uma entrada de diário:
@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
Esta função de visualização não usa os decoradores @body
e @response
, pois não há entradas ou saídas para este endpoint da API. Se a entrada de diário for excluída com sucesso, um código de status 204 (Sem conteúdo) será retornado sem dados.
Para testar as coisas, em uma janela de terminal, configure o aplicativo Flask e execute o servidor de desenvolvimento:
(venv) $ export FLASK_APP=app.py
(venv) $ export FLASK_ENV=development
(venv) $ flask run
Então, em uma janela de terminal diferente, você pode interagir com a API. Sinta-se à vontade para usar sua ferramenta de escolha aqui, como cURL, HTTPie , Requests ou Postman .
Exemplo de solicitações:
$ 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)
Quer testar os endpoints da API com mais facilidade? Confira este script , que adiciona comandos da CLI para interagir com os endpoints da API para recuperar, criar, atualizar e excluir entradas de diário.
Um recurso incrível do APIFairy é a criação automática de documentação da API!
Há três aspectos principais para configurar a documentação da API:
Já abordamos o primeiro item na seção anterior, pois incluímos as docstrings para cada função de visualização. Por exemplo, a journal()
função de visualização tem uma breve descrição da finalidade deste endpoint da API:
@journal_api_blueprint.route('/', methods=['GET'])
@response(entries_schema)
def journal():
"""Return all journal entries"""
return messages
Em seguida, precisamos incluir a docstring para descrever o projeto geral no topo do arquivo 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
"""
...
Esta docstring é usada para descrever o projeto geral, incluindo a funcionalidade chave fornecida e os principais pacotes Python usados pelo projeto.
Por fim, algumas variáveis de configuração precisam ser definidas para especificar a aparência da documentação da API. Atualize a create_app()
função em 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
Pronto para ver a documentação do projeto? Inicie o servidor de desenvolvimento Flask via flask run
e navegue até http://127.0.0.1:5000/docs para ver a documentação da API criada pela APIFairy:
No painel esquerdo, há uma lista de endpoints de API para o journal_api
blueprint. Clicar em um dos endpoints mostra todos os detalhes sobre esse endpoint:
O que é incrível nessa documentação da API é a capacidade de ver como os endpoints da API funcionam (supondo que o servidor de desenvolvimento do Flask esteja em execução). No painel direito da documentação, insira um índice de lançamento contábil manual e clique em "Enviar solicitação de API". A resposta da API é então exibida:
Esta documentação interativa torna mais fácil para os usuários entenderem a API!
Para fins de demonstração, um banco de dados SQLite será usado neste tutorial.
Como o Flask-SQLAlchemy já estava instalado no início deste tutorial, precisamos configurá-lo no arquivo project/__init__.py .
Comece criando um SQLAlchemy()
objeto na seção 'Configuração':
...
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!!
...
Em seguida, atualize a create_app()
função para especificar as variáveis de configuração necessárias:
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
Adicione a importação ao topo:
import os
A SQLALCHEMY_DATABASE_URI
variável de configuração é fundamental para identificar a localização do banco de dados SQLite. Para este tutorial, o banco de dados é armazenado em instance/app.db .
Por fim, atualize a initialize_extensions()
função para inicializar o objeto Flask-SQLAlchemy:
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!!
Quer saber mais sobre como este aplicativo Flask está conectado? Confira meu curso sobre como construir, testar e implantar um aplicativo Flask:
Crie um novo arquivo project/models.py para definir a tabela de banco de dados para representar as entradas de diário:
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}>'
Essa nova classe, Entry
, especifica que a entries
tabela do banco de dados conterá dois elementos (por enquanto!) para representar uma entrada de diário:
id
- a chave primária ( primary_key=True
) para a tabela, o que significa que é um identificador exclusivo para cada elemento (linha) na tabelaentry
- string para armazenar o texto do lançamento contábil manualEmbora o models.py defina a tabela do banco de dados, ele não cria as tabelas no banco de dados SQLite. Para criar as tabelas, inicie o shell Flask em uma janela de terminal:
(venv)$ flask shell
>>> from project import database
>>> database.drop_all()
>>> database.create_all()
>>> quit()
(venv)$
Como estamos progredindo para usar um banco de dados SQLite, comece excluindo o temporário database
(lista Python) que foi definido em 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.')
]
Em seguida, precisamos atualizar cada endpoint da API (ou seja, as funções de visualização) para utilizar o banco de dados SQLite.
Comece atualizando a journal()
função de visualização:
@journal_api_blueprint.route('/', methods=['GET'])
@response(entries_schema)
def journal():
"""Return all journal entries"""
return Entry.query.all()
A lista completa de entradas de diário agora é recuperada do banco de dados SQLite. Observe como os esquemas ou decoradores para esta função de visualização não precisaram ser alterados... apenas o processo subjacente para alterar os usuários!
Adicione a importação:
from project.models import Entry
Em seguida, atualize a add_journal_entry()
função de visualização:
@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
As entradas para esta função de visualização são especificadas por 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()
A entry
string é usada para criar uma nova instância da Entry
classe (definida em models.py ) e esta entrada de diário é então adicionada ao banco de dados.
Adicione a importação:
from project import database
A seguir, atualize 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
Esta função agora tenta pesquisar a entrada de diário especificada (com base no index
):
entry = Entry.query.filter_by(id=index).first_or_404()
Se a entrada existir, ela será devolvida ao usuário. Se a entrada não existir, um erro 404 (Não encontrado) será retornado.
A seguir, atualize 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
A update_journal_entry()
função de visualização agora tenta recuperar a entrada de diário especificada:
entry = Entry.query.filter_by(id=index).first_or_404()
Se a entrada de diário existir, a entrada é atualizada com o novo texto e salva no banco de dados.
Por fim, atualize 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
Se a entrada de diário especificada for encontrada, ela será excluída do banco de dados.
Execute o servidor de desenvolvimento. Teste cada um dos endpoints para garantir que eles ainda funcionem.
Como este projeto Flask é uma API, os códigos de erro devem ser retornados no formato JSON em vez do formato HTML típico.
No projeto Flask, isso pode ser feito usando um manipulador de erros personalizado. Em project/__init__.py , defina uma nova função ( register_error_handlers()
) na parte inferior do arquivo:
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
Essa função registra um novo manipulador de erros para quando um HTTPException
é gerado para converter a saída no formato JSON.
Adicione a importação:
from werkzeug.exceptions import HTTPException
Além disso, atualize a função Application Factory, create_app()
, para chamar essa nova função:
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
Autenticação é o processo de validação da identidade de um usuário que tenta acessar um sistema, que neste caso é a API.
A autorização, por outro lado, é o processo de verificar a quais recursos específicos um usuário específico deve ter acesso.
APIFairy utiliza Flask-HTTPAuth para suporte de autenticação. Neste tutorial, usaremos o Flask-HTTPAuth de duas maneiras:
A autenticação de token usada via Flask-HTTPAuth é frequentemente chamada de autenticação de portador, pois o processo invoca a concessão de acesso ao "portador" do token. O token deve ser incluído nos cabeçalhos HTTP no cabeçalho Authorization, como "Authorization: Bearer ".
O diagrama a seguir ilustra um fluxo típico de como um novo usuário interage com o aplicativo para recuperar um token de autenticação:
Como o Flask-HTTPAuth já estava instalado quando o APIFairy foi instalado no início deste tutorial, precisamos apenas configurá-lo no arquivo project/__init__.py .
Comece criando objetos separados para a autenticação básica e de token:
...
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!!
...
Nenhuma atualização adicional é necessária em project/__init__.py .
Em project/models.py , um novo User
modelo precisa ser criado para representar um usuário:
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}>'
Adicione as importações:
import secrets
from datetime import datetime, timedelta
from werkzeug.security import check_password_hash, generate_password_hash
O User
modelo usa werkzeug.security
para fazer o hash da senha do usuário antes de armazená-la no banco de dados.
Lembre-se: Nunca armazene a senha de texto simples em um banco de dados!
O User
modelo usa secrets
para gerar um token de autenticação para um usuário específico. Este token é criado no generate_auth_token()
método e inclui uma data/hora de expiração de 60 minutos no futuro:
def generate_auth_token(self):
self.auth_token = secrets.token_urlsafe()
self.auth_token_expiration = datetime.utcnow() + timedelta(minutes=60)
return self.auth_token
Existe um método estático, verify_auth_token()
, que é usado para verificar o token de autenticação (considerando o tempo de expiração) e retornar o usuário de um token válido:
@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
Mais um método de interesse é revoke_auth_token()
, que é usado para revogar o token de autenticação para um usuário específico:
def revoke_auth_token(self):
self.auth_token_expiration = datetime.utcnow()
Para estabelecer o relacionamento um para muitos entre o usuário ("um") e suas entradas ("muitos"), o Entry
modelo precisa ser atualizado para vincular as tabelas e 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}>'
O User
modelo já contém o link de volta para a entries
tabela:
entries = database.relationship('Entry', backref='user', lazy='dynamic')
A funcionalidade de gerenciamento de usuários do projeto Flask será definida em um Blueprint separado chamado users_api_blueprint
.
Comece criando um novo diretório em "project" chamado "users_api". Dentro desse diretório, crie um arquivo __init__.py :
from flask import Blueprint
users_api_blueprint = Blueprint('users_api', __name__)
from . import authentication, routes
Este novo Blueprint precisa ser registrado com o Flask app
em projects/__init__.py dentro da register_blueprints()
função:
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!!
Para usar o Flask-HTTPAuth, várias funções precisam ser definidas para lidar com a verificação das credenciais do usuário.
Crie um novo arquivo project/users_api/authentication.py para lidar com a autenticação básica e a autenticação de token.
Para a autenticação básica (verificar o e-mail e a senha de um usuário):
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'}
A verify_password()
função é usada para verificar se um usuário existe e se sua senha está correta. Esta função será usada pelo Flask-HTTPAuth para verificar a senha quando a autenticação básica for necessária (graças ao @basic_auth.verify_password
decorador).
Além disso, um manipulador de erros é definido para a autenticação básica que retorna informações sobre o erro no formato JSON.
Para a autenticação do token (processando um token para determinar se o usuário é válido):
@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
A verify_token()
função é usada para verificar se um token de autenticação é válido. Esta função será usada pelo Flask-HTTPAuth para verificar o token quando a autenticação do token for necessária (graças ao @token_auth.verify_token
decorador).
Além disso, um manipulador de erros é definido para a autenticação de token que retorna informações sobre o erro no formato JSON.
No users_api_blueprint
, haverá duas rotas:
Para começar, um novo conjunto de esquemas (usando marshmallow) precisa ser definido em projects/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()
Esses esquemas serão usados para definir as entradas e saídas para as funções de visualização definidas neste arquivo.
Em seguida, defina a função de visualização para registrar um novo usuário:
@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
Adicione as importações:
from apifairy import authenticate, body, other_responses, response
from project import basic_auth, database, ma
from project.models import User
Esse endpoint da API usa o new_user_schema
para especificar que o email e a senha são as entradas.
OBSERVAÇÃO: como o email e a senha são enviados para esse endpoint da API, é um bom momento para lembrar que o uso de HTTP é aceitável durante o teste de desenvolvimento, mas HTTPS (seguro) sempre deve ser usado na produção.
O email e a senha (definidos como os kwargs
argumentos da palavra-chave -) são então descompactados para criar um novo User
objeto, que é salvo no banco de dados:
new_user = User(**kwargs)
database.session.add(new_user)
database.session.commit()
A saída do endpoint da API é definida por user_schema
, que é o ID e o email do novo usuário.
A outra função de visualização a ser definida em projects/users_api/routes.py é para recuperar o token de autenticação:
@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)
O @authenticate
decorador é usado pela primeira vez neste tutorial e especifica que a autenticação básica deve ser usada para proteger esta rota:
@authenticate(basic_auth)
Quando o usuário deseja recuperar seu token de autenticação, ele precisa enviar uma solicitação POST para esse endpoint da API com o email e a senha incorporados no cabeçalho 'Authorization'. Como exemplo, o seguinte comando Python usando o pacote Requests pode ser feito para este endpoint da API:
>>> import requests
>>> r = requests.post(
'http://127.0.0.1:5000/users/get-auth-token',
auth=('pkennedy@hey.com', 'FlaskIsAwesome123')
)
Se a autenticação básica for bem-sucedida, a função de visualização recupera o usuário atual usando o current_user()
método fornecido pelo Flask-HTTPAuth:
user = basic_auth.current_user()
Um novo token de autenticação é criado para esse usuário:
token = user.generate_auth_token()
E esse token é salvo no banco de dados para que possa ser usado para autenticar o usuário no futuro (pelo menos pelos próximos 60 minutos!).
Por fim, o novo token de autenticação é retornado para o usuário salvar para todas as chamadas de API subsequentes.
Com um processo de autenticação em vigor, é hora de adicionar alguns guardas aos terminais de API existentes para garantir que apenas usuários válidos possam acessar o aplicativo.
Essas atualizações são para as funções de visualização definidas em projects/journal_api/routes.py .
Primeiro, atualize journal()
para retornar apenas as entradas de diário do usuário atual:
@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()
Atualize as importações no topo da seguinte forma:
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
O @authenticate
decorador especifica que a autenticação de token precisa ser usada ao acessar esse endpoint da API. Como exemplo, a seguinte solicitação GET pode ser feita usando Requests ( após a recuperação do token de autenticação ):
>>> import requests
>>> headers = {'Authorization': f'Bearer {auth_token}'}
>>> r = requests.get('http://127.0.0.1:5000/journal/', headers=headers)
Depois que o usuário é autenticado, a lista completa de entradas de diário é recuperada do banco de dados com base no ID do usuário:
user = token_auth.current_user()
return Entry.query.filter_by(user_id=user.id).all()
A saída desse endpoint da API é definida pelo @response
decorador, que é uma lista de entradas de diário (ID, entrada, ID do usuário).
A seguir, atualize 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
Assim como na função de visualização anterior, o @authenticate
decorador é usado para especificar que a autenticação de token precisa ser usada ao acessar esse endpoint da API. Além disso, a entrada de diário agora é adicionada especificando o ID do usuário que deve ser associado à entrada de diário:
user = token_auth.current_user()
new_message = Entry(user_id=user.id, **kwargs)
A nova entrada de diário é salva no banco de dados e a entrada de diário é retornada (conforme definido pelo @response
decorador).
A seguir, atualize 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
O @authenticate
decorador é adicionado para especificar que a autenticação de token é necessária para acessar esse endpoint da API.
Ao tentar recuperar uma entrada de diário, uma verificação adicional é adicionada para garantir que o usuário que está tentando acessar a entrada de diário seja o "proprietário" real da entrada. Caso contrário, um código de erro 403 (Proibido) é retornado por meio da abort()
função do Flask:
if entry.user_id != user.id:
abort(403)
Observe que este endpoint de API tem duas respostas fora do nominal especificadas pelo @other_responses
decorador:
@other_responses({403: 'Forbidden', 404: 'Entry not found'})
Lembrete: O
@other_responses
decorador serve apenas para documentação; é responsabilidade da função view gerar esses erros.
A seguir, atualize 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
As atualizações desta função de visualização são semelhantes às outras funções de visualização nesta seção:
@authenticate
decorador especifica que a autenticação de token é necessária para acessar este endpoint da APIPor fim, atualize 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
Este tutorial forneceu um passo a passo de como construir uma API de maneira fácil e rápida no Flask usando APIFairy.
Os decoradores são a chave para definir os endpoints da API:
@arguments
- argumentos de entrada da string de consulta da URL@body
- estrutura da solicitação JSON@response
- estrutura da resposta JSON@authenticate
- abordagem de autenticação usando Flask-HTTPAuth@other_responses
- respostas fora do nominal, como códigos de erro HTTPAlém disso, a documentação da API gerada pelo APIFairy é excelente e fornece informações importantes para os usuários do aplicativo.
Fonte: https://testdrive.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