05 - Serviços Web em Python (1 Parte)
05 - Serviços Web em Python (1 Parte)
1. Serviços RESTful
REST significa Representational State Transfer e foi um termo cunhado por Roy Fielding na sua
dissertação de doutoramento para descrever um estilo de arquitetura simples, orientada a
recursos que sustentam a web.
Deve-se notar que, embora o Fielding tivesse como objetivo criar uma forma de descrever o
padrão de comportamento dentro da web, ele também mirava em produzir serviços “mais
leves” baseados na web (em contrapartida, aos serviços que usam o Enterprise frameworks de
integração ou serviços baseados em SOAP). Estes serviços web (mais leves), baseados em HTTP
tornaram-se muito populares e agora são amplamente utilizados em muitas áreas. Os sistemas
que seguem esses princípios são chamados de serviços RESTful. Um aspeto chave de um serviço
RESTful é que todas as interações entre um cliente (quer se trata de código JavaScript a rodar
num browser ou de uma aplicação autónoma) são realizadas usando operações simples
baseadas no protocolo HTTP.
O HTTP suporta quatro operações (ou métodos), sendo elas: o HTTP Get; o HTTP Post; o HTTP
Put e o HTTP Delete. Estas podem ser usadas como palavras para indicar o tipo de ação que está
sendo solicitada. Normalmente são usadas como segue:
Deve-se notar que o REST não é um padrão da mesma forma que o HTML. Em vez disso, trata-
se de um padrão de design que pode ser usado para criar aplicações web que podem ser
invocados por HTTP e que dão sentido ao uso dos métodos Get, Post, Put e Delete em relação a
um recurso específico (ou tipo de dados).
As vantagens de usar serviços RESTful como tecnologia, em comparação com algumas outras
abordagens (como serviços baseados em SOAP que também podem ser invocados por HTTP)
são as seguintes:
1
(Baseado no livro: J. Hunt, Advanced Guide to Python 3 Programming, Undergraduate Topics in Computer Science)
1
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
O que significa que os custos são mais baixos, quer do lado do cliente, quer do lado do servidor.
Significa, também, uma menor dependência quer das licenças comercial, quer da tecnologia,
uma vez que os programadores não precisam saber nada sobre os detalhes de implementação
ou tecnologias que estão sendo usadas para criar os serviços.
2. Podem ser livros, produtos numa loja, reservas de quartos em hotéis etc. (por exemplo, um
serviço relacionado com uma biblioteca que pode fornecer informações sobre recursos como
livros, CDs, DVDs, etc. Neste serviço, os livros são apenas um tipo de recurso. Para simplificar,
no exemplo que de seguida iremos fazer referência, iremos ignorar os outros recursos, como
DVDs e CDs, etc.
Onde o ISBN (International Standard Book Number) indica um número único a ser usado para
identificar um livro específico cujos detalhes serão devolvidos usando esse URL.
Vamos precisar de conceber a representação ou os formatos que o serviço pode fornecer. Estes
podem incluir texto simples, JSON, XML, etc. Padrões JSON para o JavaScript Object Notation
representam uma maneira concisa de descrever dados que devem ser transferidos de um
serviço a correr num servidor para um cliente que se executa num browser. Este é o formato
que de seguida iremos fazer uso. Como parte disso, podemos identificar uma série de operações
a serem fornecidas pelos nossos serviços, com base no tipo de Método HTTP usado para invocar
o serviço e o respetivo conteúdo do URL fornecido. Por exemplo, para um simples BookService
pode ser:
• GET /book/list — usado para procurar/ler todos os livros atuais no formato JSON.
• PUT /book (JSON no corpo da mensagem) — usado para atualizar os dados mantidos
em um livro que já existe.
2
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Note-se que o parâmetro isbn nas URLs acima indicados, na verdade faz parte do caminho do
URL.
• Flask,
• Django,
• Web2py e
• CherryPy
4. Flask
Flask é uma framework de desenvolvimento web para Python. É também caracterizada como
uma micro framework para Python, que segundo a Flask está relacionado com o objetivo
principal de manter o núcleo do Flask simples, mas extensível. Ao contrário do Django, esta não
inclui recursos destinados a ajudar a integrar as aplicações com base de dados, por exemplo. Em
vez disso, o Flask concentra-se na funcionalidade principal exigido de uma estrutura de serviços
web e permite que extensões sejam incluídas e usadas quando necessário para a inclusão de
funcionalidades adicionais.
O Flask representa também uma convenção para a estrutura de configuração; isto é, se forem
seguidas as convenções padrão, então não será necessário lidar com muitas informações de
configurações adicionais (embora se for o caso de se desejar seguir um conjunto diferente de
convenções, então poder-se-á fornecer outras informações de configuração para alterar os
padrões iniciais). Quanto mais as pessoas (pelo menos inicialmente) seguirem essas convenções,
mais fácil e rapidamente se torna a obtenção de aplicações a funcionar.
3
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
• Uma coleção de pares nome/valor em que o nome e o valor são separados por dois
pontos ':' e cada par pode ser separado por uma vírgula ','.
• Uma lista ordenada de valores que estão entre parêntesis retos ('[]').
Isso torna muito fácil construir estruturas que representam qualquer conjunto de dados. Por
exemplo, um livro com ISBN, título (title), autor (author) e preço (price) pode ser representado
por:
{
"author": "Phoebe Cooke",
"isbn": 2,
"price": 12.99,
"title": "Java"
}
Por sua vez, uma lista de livros pode ser representada por um conjunto de livros separados por
vírgulas dentro de parênteses retos. Por exemplo:
[ {"author": "Gryff Smith","isbn": 1, "price": 10.99, "title":
"XML"},
{"author": "Phoebe Cooke", "isbn": 2, "price": 12.99, "title":
"Java"}
{"author": "Jason Procter", "isbn": 3, "price": 11.55, "title":
"C#"}]
1. Importaçao do Flask.
3. Implementar uma ou mais funções (ou métodos) para dar suporte aos serviços que
se pretende publicar.
4. Fornecer informações de roteamento para rotear da URL para uma função (ou
método).
4
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Em seguida, precisamos criar o objeto da aplicação principal que é uma instância da classe
Flask:
O objeto de aplicativo Flask implementa o WSGI (Web Server Gateway Interface) padrão para
Python. Ele fornece uma simples convenção de como os servidores da Web devem lidar com
solicitações das aplicações. O objeto de aplicação Flask é o elemento que pode rotear um pedido
de URL para uma função Python.
Por exemplo, no código seguinte, o decorador @app.route mapeia a URL ‘/hello’ para a
função welcome() para solicitações HTTP Get:
• A segunda coisa é que vamos retornar os nossos dados usando o formato JSON; portanto,
usamos a função jsonify() e passamos-lhe uma estrutura do Dicionário Python com um único par
5
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
chave/valor. Neste caso a chave é ‘msg’ e os dados a esta associados é 'Hello Flask
World'. A função jsonify() converterá esta estrutura de dados Python numa estrutura
JSON equivalente.
Opcionalmente, este método tem um parâmetro de depuração de palavra-chave que pode ser
definido como True. Se for esta a opção escolhida, durante o arranque da aplicação, algumas
informações de depuração são geradas permitindo visualizar o que está a acontecer. Isto pode
ser útil na fase de desenvolvimento, mas normalmente não é usado na fase de produção.
É claro que ainda não visualizamos nenhuma saída do nosso próprio programa. Isto acontece
porque ainda não invocamos a função welcome() por meio da URL ‘/hello’.
6
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
O restante do URL deve fornecer as informações que permitirão ao Flask fazer o roteamento do
computador e porto para as funções que queremos executar.
Uma característica útil desta abordagem é que se for feita uma alteração no programa a
framework Flask anotará essa alteração quando se executa no modo de desenvolvimento
(development mode), podendo reiniciar o serviço web com as alterações de código implantadas.
Caso isso seja feito, o output notificará a ocorrência da alteração do programa:
Isto permite que as alterações sejam feitas em tempo real e os seus efeitos podem ser
imediatamente vistos.
7
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
6. Referências
8
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
2. O Design
Antes de olharmos para a implementação da API RESTful Bookshop, vamos considerar quais
elementos temos para os serviços de serviços.
Uma questão que muitas vezes causa alguma confusão é como os serviços web se relacionam
com abordagens de design tradicionais, como o design orientado a objetos. A abordagem
adotada aqui é que a API do serviço web fornece uma maneira de implementar uma interface
para funções, objetos e métodos apropriados usados para implementar a aplicação e centrado
no modelo de domínio1.
Isto significa que ainda teremos que definir classes que representarão a Livraria (Bookshop) e os
Livros (Books) mantidos na livraria. Por sua vez, as funções que implementam os serviços web
acederão à livraria para procurar/pesquisar, alterar, atualizar e remover livros da livraria.
Pode-se observar que um objeto Book terá como atributos, um isbn, um título (title), um autor
(author) e um preço (price), respetivamente.
Por sua vez, o objeto Bookshop terá um atributo books que poderá ter zero ou mais Books. O
atributo books irá conter uma Lista (List), considerando que a lista de livros muda
dinamicamente à medida que novos livros são adicionados ou livros antigos são removidos.
A Bookshop também definirá os três seguintes métodos que irão permitir que um livro seja:
1
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
• get_books() que mapeia para a URL /book/list usando o método HTTP Get
• create_book() que mapeia para a URL /book usando o método HTTP Post.
• update_book() que mapeia para a URL /book mas usando o método HTTP Put.
• delete_book() que mapeia para a URL /book/<isbn> mas usando o método HTTP
Delete.
3. O Modelo de Domínio
O modelo de domínio compreende as classes Book e Bookshop. Estas são apresentados a seguir.
A classe Book é uma classe simples classe de tipo Value (ou seja, é orientada a dados sem
comportamento próprio):
class Book:
def __init__(self, isbn, title, author, price):
self.isbn = isbn
self.title = title
self.author = author
self.price = price
def __str__(self):
return self.title + ' by ' + self.author + ' @ ' + str(self.price)
A classe Bookshop contém uma lista de livros e fornece um conjunto de métodos para aceder a
livros, atualizar livros e remover livros:
class Bookshop:
def __init__(self, books):
self.books = books
def get(self, isbn):
if int(isbn) > len(self.books):
abort(404)
return list(filter(lambda b: b.isbn == isbn, self.books))[0]
def add_book(self, book):
self.books.append(book)
def delete_book(self, isbn):
self.books = list(filter(lambda b: b.isbn != isbn, self.books))
2
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
bookshop = Bookshop(
[Book(1, 'XML', 'Gryff Smith', 10.99),
Book(2, 'Java', 'Phoebe Cooke', 12.99),
Book(3, 'Scala', 'Adam Davies', 11.99),
Book(4, 'Python', 'Jasmine Byrne', 15.99)])
class Book:
""" Represents a book in the bookshop"""
def __init__(self, isbn, title, author, price):
self.isbn = isbn
self.title = title
self.author = author
self.price = price
def __str__(self):
return self.title + ' by ' + self.author + ' @ ' + str(self.price)
def to_json(self):
return {
'isbn': self.isbn,
'title': self.title,
'author': self.author,
'price': self.price
}
Sendo assim, podemos usar este método com a função jsonify() para converter um Book
no formato JSON:
jsonify({'book': book.to_json()})
Esta abordagem fornece uma maneira simples de converter um Book em JSON, significando
que sempre que precisarmos de fazer a conversão temos que nos lembrar de chamar o método
to_json(). Em alguns casos, isso significa que também teremos que escrever um código um
3
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
pouco complicado. Para exemplo, consideremos que pretendemos retornar uma lista de livros
(books) da Livraria (Bookshop) como uma lista JSON, para tal podemos escrever:
Aqui usamos uma compreensão de lista para gerar uma lista contendo versões de JSON dos
books (livros) mantidos na Bookshop (livraria), o que poderá começar a parecer
excessivamente complexo, fácil de esquecer e, provavelmente, propenso à ocorrência de erros.
O próprio Flask usa codificadores para codificar tipos em JSON. Oferecendo uma maneira de se
criar codificadores próprios para serem usados na conversão de um tipo personalizado, como a
classe Book, em JSON. Tal codificador pode ser usado automaticamente pela função
jsonify().
Para fazer isso, devemos implementar uma classe de codificador; a classe estenderá a
superclasse flask.json.JSONEncoder.
A classe deve definir um método default(self, obj). cuja função é a de pegar um objeto
e retornar a representação JSON desse objeto.
Podemos, portanto, escrever um codificador para a classe Book da seguinte forma:
class BookJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, Book):
return {
'isbn': obj.isbn,
'title': obj.title,
'author': obj.author,
'price': obj.price
}
else:
return super(BookJSONEncoder, self).default(obj)
O método default() desta classe verifica se o objeto que lhe é passado é uma instância da classe
Book e, caso o seja, criará uma versão JSON de Book. Essa estrutura JSON é baseada nos
atributos isbn, title, author e price. Se não for uma instância da classe Book, então esse objeto
é passado à classe pai.
Agora podemos registar este codificador no objeto de aplicação Flask para que ser usado sempre
que um Book precisar de ser convertido em JSON. Isto é feito atribuindo o codificador
personalizado ao objeto da aplicação Flask por meio do atributo app.json_encoder:
app = Flask(__name__)
app.json_encoder = BookJSONEncoder
Se desejarmos codificar um único livro (Book) ou uma lista de livros (books), isso pode ser feito
automaticamente sem precisarmos de fazer mais nada. Para o efeito, e em relação aos exemplos
anteriormente descritos, podemos simplesmente referenciar o atributo book ou o atributo
bookshop.books da seguinte maneira:
jsonify({'book': book})
jsonify({'books': bookshop.books})
4
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
@app.route('/book/list', methods=['GET'])
def get_books():
return jsonify({'books': bookshop.books})
@app.route('/book/<int:isbn>', métodos=['GET'])
def get_book(isbn):
livro = livraria.get(isbn)
return jsonify({'book': book})
A primeira função retorna a lista de livros (books) da livraria (bookshop) numa estrutura JSON
usando a chave books.
A segunda função recebe um número isbn como parâmetro. Este é um parâmetro URL; que
constitui uma parte da URL usada para invocar esta função e assume valores variáveis a serem
passados para à função. Por conseguinte, um utilizador pode solicitar detalhes de livros com
diferentes ISBNs, alterando apenas o elemento ISBN do URL. Por exemplo:
• /book/1 indica que queremos obter informações sobre o livro com ISBN 1.
• /book/2 indica que queremos obter informações sobre o livro com ISBN 2.
No Flask para indicar que algo é um parâmetro de URL usam-se os símbolos (<>). Estes delimitam
o nome do parâmetro URL e permitem que o mesmo seja passado para a função mantendo o
mesmo nome.
No exemplo anterior, também foi (opcionalmente) indicado o tipo do parâmetro. Por omissão,
o tipo será uma string; no entanto, sabemos que o ISBN é, na verdade, um inteiro e, portanto,
tivemos de explicitar o tipo int seguido de : (dois pontos) antes do nome do parâmetro (‘isbn’).
Neste contexto existem várias outras opções disponíveis, tais como:
• string (por omissão),
• int (para valores inteiros),
• float (para valores positivos em vírgula flutuante)
• uuid (para strings de tipo uuid2)
• path (que não gosta de string, mas aceita barras).
Podemos usar novamente um browser para visualizar os resultados da chamada desses serviços.
desta vez os URLs são, respetivamente:
• http://127.0.0.1:5000/book/list e
• http:/127.0.0.1:5000/book/1
Por exemplo:
2
https://pt.wikipedia.org/wiki/Identificador_%C3%BAnico_universal
5
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Como se pode ver, as informações do(s) livro(s) são retornadas no formato JSON como um
conjunto de pares chave/valor.
O serviço web de remoção de um livro é muito semelhante ao serviço GET (para obtenção de
informações de um livro), pois usa um isbn como parâmetro de caminho de URL. No entanto,
este caso, ele apenas retorna uma confirmação de que o livro foi removido com sucesso da
livraria:
@app.route('/book/<int:isbn>', methods=['DELETE'])
def delete_book(isbn):
bookshop.delete_book(isbn)
return jsonify({'result': True})
Com um browser não é possível testar se o livro foi removido, uma vez que este utiliza apenas
HTTP Get para todos os URLs nele inseridos. No entanto, o serviço web relativo à remoção está
associado ao pedido do método HTTP Delete.
Para invocar a função delete_book(), precisamos garantir que o pedido enviado usa o método
DELETE. Isto pode ser feito a partir de um cliente capaz de poder indicar o tipo de método que
pretende usar no pedido HTTP. Exemplos que concretizem isto podem ser a implementação de
outro programa em Python, um site implementado em JavaScript, etc.
Aqui e para fins de teste, vamos usar programa cur3l, que se encontra disponível na maioria dos
sistemas Linux e Mac e pode ser facilmente instalado noutros sistemas operativos.
O curl é uma ferramenta de linha de comando e biblioteca que pode ser usada para enviar e
receber dados pela internet. Suporta uma ampla gama de protocolos e padrões e, em particular,
suporta os protocolos HTTP e HTTPS e pode ser usado para enviar e receber dados por HTTP/S
usando diferentes métodos associados a pedidos destes protocolos.
3
https://pt.wikipedia.org/wiki/CURL#cURL
6
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Por exemplo, para invocar a função delete_book() usando a URL /book/2 e o método HTTP
Delete podemos usar o curl da seguinte forma:
"result": true
Podemos verificar a ocorrência de remoção do livro, através de um browser com o URL /book/list
:
7. Inserção de um Livro
Também vamos precisar de inserir de um novo livro (book) na Livraria (BookShop). Os detalhes
de um novo livro podem ser passados ao URL como parâmetros de caminho de URL; porém
como a quantidade de dados a serem adicionados cresce, esta operação se tornaria cada vez
mais difícil de manter e verificar. De facto, embora historicamente houvesse um limite de 2083
caracteres no Internet Explore (IE) da Microsoft, que teoricamente foi removido desde o IE8, na
prática, normalmente ainda há limites no tamanho da URL. A maioria dos web servers têm um
limite de 8 KB (ou 8192 bytes) para o campo URL, embora isso seja normalmente configurável.
Também pode haver limites do lado do cliente (como os impostos pelo IE ou o Safari da Apple,
(que geralmente têm um limite de 2 KB). Geralmente, se num browser ou servidor esse limite
for excedido, os caracteres fora do limite serão truncados (em alguns casos sem qualquer aviso).
7
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Normalmente, esses dados são enviados no corpo do pedido HTTP como parte de um HTTP Post
Request. O limite do tamanho de uma URL no corpo de uma mensagem HTTP Post Request é
muito superior (geralmente até 2 GB). Isso significa que é uma maneira muito mais confiável e
seguro de transferir dados para um serviço web.
No entanto, deve-se notar que isso não significa que os dados estão mais seguros do que através
do URL; apenas que são enviados de uma maneira diferente.
Do ponto de vista das funções Python que são invocadas como resultado de um pedido do
método HTTP Post, significa que os dados não estão disponíveis como parâmetro para a URL e,
portanto, para a função. Em vez disso, dentro da função é necessário obter o objeto do pedido
e, em seguida, usá-lo para obter as informações mantidas dentro do corpo do pedido.
Um atributo-chave no pedido de objeto, disponível quando uma solicitação HTTP contém dados
JSON, é o request.json. Este contém um dicionário como estrutura com os valores associados às
chaves da estrutura de dados JSON.
A função acima acede ao objeto flask.request que representa o atual pedido HTTP. A função
primeiro verifica se contém dados JSON e que o ISBN do livro a ser adicionado faz parte dessa
estrutura JSON. Em caso negativo, então é chamada a função flask.abort() passando como
resposta um adequado HTTP Status Code. Neste caso em concreto, foi passado à resposta o
código de erro de Bad Request (código de erro HTTP 400).
Se, no entanto, os dados JSON estiverem presentes e contiverem um número ISBN, os valores
para as chaves isbn, title, author e price são obtidos. Convém lembrar que o JSON é uma
estrutura tipo dicionário composta de chaves e valores, que facilita a extração dos dados que
uma estrutura JSON contém. Significa também que podemos ter tipos de acesso baseados quer
em métodos, quer em chaves. Isto é mostrado acima onde utilizamos o método get( ) junto com
um valor padrão a ser usado, se um autor (author) não for especificado.
8
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Finalmente, como queremos tratar o preço como um número de tipo vírgula flutuante (floating
point), devemos usar o função float( ) para converter o formato string fornecido pelo JSON em
um float.
Usando os dados extraídos podemos criar uma nova instância Book que pode ser inserido na
livraria. Como é comum em web services, retorna-se o novo objeto livro criado como resultado
da criação do livro junto com a resposta HTTP Code Staus Nº 201, que indica a criação bem-
sucedida de um recurso.
Agora podemos testar este serviço usando o programa de linha de comando curl:
curl -H "Content-Type: application/json" -X POST -d
'{"title":"Read a book", "author":"Bob ","isbn":"5", "price":"3.44"}'
http://localhost:5000/book
As opções usadas neste comando indicam o tipo de dados que está a ser enviado no corpo (body)
do pedido (-H) juntamente com os dados a incluir no corpo do pedido (-d). O resultado da
execução deste comando é:
{
"book": {
"author": "Bob",
"isbn": "5",
"price": "3.44",
"title": "Read a book"
}
}
A atualização de um livro que já está em posse do objeto livraria é muito semelhante à operação
de inserção, exceto que o método usado pelo pedido é o HTTP Put.
Mais uma vez, a função que implementa o comportamento requerido deve usar um pedido flask
para aceder aos dados enviados junto com o pedido PUT. Contudo, neste caso, o número ISBN
especificado é usado para localizar o livro que se pretende atualizar, em vez de especificar um
livro completamente novo. O código da função update_book() é o seguinte:
@app.route('/book', methods=['PUT'])
def update_book():
if not request.json or not 'isbn' in request.json:
abort(400)
isbn = request.json['isbn']
book = bookshop.get(isbn)
book.title = request.json['title']
book.author = request.json['author']
book.price = request.json['price']
return jsonify({'book': book}), 201
9
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
Esta função altera o title, author e price de um livro da livraria, retornando-o atualizado como
resultado da execução da função.
O programa curl pode ser usado novamente para invocar esta função, sendo que desta vez o
método usado é o HTTP Put:
curl -H "Content-Type: application/json" -X PUT -d
O código apresentado para os serviços web da livraria não faz as devidas validações, pois é
possível tentar inserir um novo livro com o mesmo ISBN de um já existente. Não obstante, pode-
se verificar se um número ISBN já foi atribuído, usando as funções create_book( ) e
update_book( ). Caso um número ISBN não foi atribuído? Em ambas as funções chamamos a
função flask.abort( ). Se isso acontecer, por omissão, uma mensagem de erro será enviada de
volta ao cliente.
10
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
O estranho aqui é que o output do erro está no formato HTML, que não é o que esperávamos,
pois estamos a cria um serviço web e a trabalhar com o JSON. O problema é que o Flask tem
como padrão gerar uma página HTML de erro que espera ser mostrado num browser.
Podemos superar esta situação definindo uma nossa função personalizada de tratamento de
erros que será uma função decorada com um decorador @app.errorhandler( ) que fornece o
código de status de resposta que ele manipula. Por exemplo:
@app.errorhandler(400)
def not_found(error):
return make_response(jsonify({'book': 'Not found'}), 400)
Agora, quando um código 400 é gerado através da função flask.abort( ), a função not_found( )
será invocada e uma resposta JSON será gerada com as informações fornecidas pela função
flask.make_response(). Por exemplo:
curl -H "Content-Type: application/json" -X POST -d
11
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
12
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
13
Sistemas Distribuídos 3º Ano LEIT ISECMAR (UTA)
14