Tutorial de Servidor Web e APIs com python
https://poseidon.les.inf.puc-rio.br/rpaskin/python_web_flask
Você pode clonar diretamente, ou melhor ainda, crie seu próprio Fork!
git@poseidon.les.inf.puc-rio.br:rpaskin/python_web_flask.git (ou o seu fork)
git cloneO objetivo desse tutorial é mostrar como podemos criar um servidor web para integrar com qualquer código Python.
Instalação Python, Virtualenv e Flask
Ambiente
Você deve ter o python3 instalado. Caso não tenha:
Por exemplo:
brew update
brew upgrade python
# OU caso não esteja instalado
brew install python
Ambiente Virtual
Um ambiente virtual cria uma configuração local ao seu diretório sem afetar o resto do sistema. Veja https://virtualenv.pypa.io/en/latest/
Para instalar caso não tenha:
pip3 install virtualenv
Crie uma pasta para seu teste e crie o ambiente virtual
mkdir flask # cria a pasta
cd flask
virtualenv flask
flask/bin/python3 -m pip install flask
Permissões
Para poder executar (rodar) um programa, ele deve ter permissões de execução, para isso use o comando chmod:
chmod a+x app.py
chmod a+x api.py
Edite o programa do servidor simples app.py
#!flask/bin/python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Olá mundo!"
if __name__ == '__main__':
app.run(debug=True)
Execute o servidor simples
Para executar use ./ (indicando o diretório atual) seguido do nome do programa:
./app.py
Acesse a URL mostrada. Qual o resultado?
Indo além
Vamos criar um outro servidor mais complexo com uma API REST.
O que é REST?
REpresentational State Transfer
REST é um estilo de arquitetura para recursos em rede, que deve obedecer a algumas diretivas:
- Interface uniforme
- Não guarda estado
- Cacheável
- Cliente-servidor
- Sistema em camadas
Verbos
Os verbos definem qual o tipo de pedido (request):
HTTP Verb | CRUD | Entire Collection (e.g. /customers) | Specific Item (e.g. /customers/{id}) |
---|---|---|---|
POST | Create | 201 (Created), 'Location' header with link to /customers/{id} containing new ID. | 404 (Not Found), 409 (Conflict) if resource already exists.. |
GET | Read | 200 (OK), list of customers. Use pagination, sorting and filtering to navigate big lists. | 200 (OK), single customer. 404 (Not Found), if ID not found or invalid. |
PUT | Update/Replace | 405 (Method Not Allowed), unless you want to update/replace every resource in the entire collection. | 200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid. |
PATCH | Update/Modify | 405 (Method Not Allowed), unless you want to modify the collection itself. | 200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid. |
DELETE | Delete | 405 (Method Not Allowed), unless you want to delete the whole collection—not often desirable. | 200 (OK). 404 (Not Found), if ID not found or invalid. |
Note que são diretivas e nem sempre são obedecidas ao pé da letra. Ou seja, você poderia implementar um Delete usando um verbo GET. Poderia mas não deveria!
Referências
- https://www.restapitutorial.com/lessons/whatisrest.html
- https://www.service-architecture.com/articles/web-services/representational_state_transfer_rest.html
- Dissertação de Roy Fielding
- https://www.restapitutorial.com/lessons/httpmethods.html
Exemplo: servidor com API Rest (GET)
#!flask/bin/python
from flask import Flask, jsonify, abort
app = Flask(__name__)
tasks = [
{
'id': 1,
'title': u'Buy groceries',
'description': u'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': u'Learn Python',
'description': u'Need to find a good Python tutorial on the web',
'done': False
}
]
@app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
if __name__ == '__main__':
app.run(debug=True)
Testando o GET
Via linha de comando (usando o curl)
Caso necessário:
$ brew install curl
Rode o comando para acessar o servidor:
$ curl -i http://localhost:5000/todo/api/v1.0/tasks
Acesse a URL mostrada. Qual o resultado? Por que? Como resolver?
Adicionando uma nova rota (GET)
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = [task for task in tasks if task['id'] == task_id]
if len(task) == 0:
abort(404)
return jsonify({'task': task[0]})
Como acessar a URL dessa nova rota?
Desafios
- Juntar as três versões no mesmo servidor (Olá mundo, /tasks e /tasks/int:task_id )
Adicionando erros mais amigáveis
from flask import make_response
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
Adicionando rota de POST
from flask import request
@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():
if not request.json or not 'title' in request.json:
abort(400)
task = {
'id': tasks[-1]['id'] + 1,
'title': request.json['title'],
'description': request.json.get('description', ""),
'done': False
}
tasks.append(task)
return jsonify({'task': task}), 201
Testando o POST via shell
$ curl -i -H "Content-Type: application/json" -X POST -d '{"title":"Ler um livro"}' http://localhost:5000/todo/api/v1.0/tasks
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 106
Server: Werkzeug/0.14.1 Python/3.6.1
Date: Tue, 11 Dec 2018 13:36:23 GMT
{
"task": {
"description": "",
"done": false,
"id": 3,
"title": "Ler um livro"
}
}
$ curl -i http://localhost:5000/todo/api/v1.0/tasks
Desafio
- Usar dados que você mesmo pode prover, a partir de outras fontes.
Exemplo
Mostrar dados de filmes do kaggle: https://www.kaggle.com/jrobischon/wikipedia-movie-plots
Instalar pandas se necessário:
$ flask/bin/python3 -m pip install pandas
Nova rota
import pandas as pd
@app.route('/movies/api/v0.1/movies', methods=['GET'])
def get_movies():
csv_file = pd.DataFrame(pd.read_csv("movies.csv", sep = ",", header = 0, index_col = False))
json_file = csv_file.to_json(orient = "records", date_format = "epoch", double_precision = 10, force_ascii = True, date_unit = "ms", default_handler = None)
response = app.response_class(
response=json_file,
status=200,
mimetype='application/json'
)
return response
Próximos passos
Se você quiser algo mais complexo, porém mais robusto usando Model-View-Controller, por exemplo, aprenda a usar o Django, por exemplo pelo tutorial do Vitor Freitas
Créditos
Tutorial de Michael Grinberg:
https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask