Executando verificação de segurança...
2

Economizar código na hora de fazer requisições com Gateway feito em Flask para um microserviço

Imagina que você tem um microserviço em feito em qualquer linguagem, e uma das rotas te possibilita fazer um CRUD, e desejas integra-lo no seu Gateway feito em Flask, mas percebe que terás que escrever linha por linha com todos os métodos de requisição, que terás que criar uma função para cada rota. Isso se torna inviavel em um cenário extremamente comum onde há no mínimo 6 rotas para a realização de um CRUD (GET, POST, GET PK, PUT, PATCH e DELETE).

Contudo, enquanto eu programava aqui, tive a ideia de criar uma classe para facilitar esse trabalho.

from src.exceptions import ImageNotAvaliable
from flask import Blueprint, request, Response, render_template
from src.utils.request_tools import Methods
from src.service.cva import CVAService
from http import HTTPStatus
import json
from .r import r
from flask import Blueprint, Response
from os import getenv
from dotenv import load_dotenv


import requests



def r(host:str,endpoint:str,method:str,data={},headers={}) -> Response:
    url = vj(host,endpoint)

    headers = {'Content-Type':'application/json',**headers}
    
    if method.upper() == Methods.GET: return requests.get(url,headers=headers)
    elif method.upper() == Methods.PUT: return requests.put(url,headers=headers,data=json.dumps(data))
    elif method.upper() == Methods.POST: return requests.post(url,headers=headers,data=json.dumps(data))
    elif method.upper() == Methods.PATCH: return requests.patch(url,headers=headers,data=json.dumps(data))
    elif method.upper() == Methods.DELETE: return requests.delete(url,headers=headers)
    
    return None

class DRFBlueprint(Blueprint):
    def __init__(self, name, import_name, url_prefix, host_env,host_endpoint,template_folder='templates'):
        super().__init__(name, import_name, url_prefix, template_folder=template_folder)
        
        load_dotenv()
        self.HOST = getenv(host_env)
        self.ENDPOINT = host_endpoint

        self.add_url_rule(url_prefix + '/', 'object_get', self.object_request, methods=[Methods.GET,Methods.POST])
        self.add_url_rule(url_prefix + '/<id>', 'object_put', self.object_request, methods=[Methods.GET,Methods.PUT,Methods.PATCH,Methods.DELETE])

    def object_request(self,id=None) -> Response:
        data = dict(request.json.items())
        
        res = r(self.HOST,self.ENDPOINT + '/' + id if id != None else self.ENDPOINT,request.method,headers=request.headers,data=data)
        return Response(
            res.text,
            status=res.status_code
        )

"Esse código define uma função chamada r que envia uma requisição HTTP para uma url composto de um host e endpoint específicos. A função aceita os parâmetros host, endpoint, method, data e headers. Ela define o tipo de conteúdo como JSON e adiciona os headers especificados à requisição. De acordo com o método especificado, a função usa o pacote requests para enviar uma requisição GET, PUT, POST, PATCH ou DELETE. Em seguida, ele retorna a resposta da requisição.

A classe DRFBlueprint é uma subclasse de Flask's Blueprint, é usada para criar um blueprint (um conjunto de rotas) para uma aplicação Flask. Ele adiciona duas rotas para o blueprint, uma rota para acessar objetos sem um ID específico e outra rota para acessar objetos com um ID específico. Essas rotas chamam o método object_request, que pega os dados da requisição e os envia para outra função r, que envia uma requisição para o host e endpoint especificados. Ele retorna a resposta recebida da requisição. Ele também carrega o arquivo .env e obtém o valor do host e do endpoint." - Explicação do ChatGPT.

No final você só precisa fazer a seguinte alteração no seu arquivo "main.py":

from flask import Flask
from src.utils.request_tools.rest_r import DRFBlueprint

app = Flask(__name__)

userCrudBlueprint = DRFBlueprint('userCrudBlueprint', __name__, url_prefix='/user/',host_env="USER_HOST",host_endpoint='api/v1/user')

app.register_blueprint(userCrudBlueprint)

"Esse código cria uma aplicação Flask chamada app e cria uma instância da classe DRFBlueprint com o nome userCrudBlueprint. Ele usa os parâmetros 'userCrudBlueprint', name, url_prefix='/user/', host_env="USER_HOST" e host_endpoint='api/v1/user' para instanciar a classe. Esse blueprint é usado para criar rotas para acessar objetos relacionados a usuários. Ele registra o blueprint no app Flask, permitindo que as rotas do blueprint sejam acessadas na aplicação." - Explicação do ChatGPT.

Conclusão

É um caso de uso muito espécifico, até porque a maioria dos devs em seus projetos pessoais não utilizam o design pattern de microservice ou são a minoria que trabalham direto em microservice e que possuem liberdade para trabalhar em quaisquer partes do código. Mas no dia em que você precisar dessa solução, graças ao SEO do Tabnews você com certeza irá achar e provavelmente adaptar com outras linguagens.

Carregando publicação patrocinada...
1

Cuidado com essa parte ↓↓↓

if method.upper() == Methods.GET: return requests.get(url,headers=headers)
elif method.upper() == Methods.PUT: return requests.put(url,headers=headers,data=json.dumps(data))
elif method.upper() == Methods.POST: return requests.post(url,headers=headers,data=json.dumps(data))
elif method.upper() == Methods.PATCH: return requests.patch(url,headers=headers,data=json.dumps(data))
elif method.upper() == Methods.DELETE: return requests.delete(url,headers=headers)

Caso o method for DELETE, ele terá que executar 5 vezes o mesmo método.

faça assim ↓↓↓

requestMethod = method.upper()

if requestMethod == Methods.GET: return requests.get(url,headers=headers)
elif requestMethod == Methods.PUT: return requests.put(url,headers=headers,data=json.dumps(data))
elif requestMethod == Methods.POST: return requests.post(url,headers=headers,data=json.dumps(data))
elif requestMethod == Methods.PATCH: return requests.patch(url,headers=headers,data=json.dumps(data))
elif requestMethod == Methods.DELETE: return requests.delete(url,headers=headers)

Aqui ele executa somente uma vez independente do method.
Daria para encaixar o match/case, mas ai é da sua escolha.

1

Bem lembrado!! As vezes esqueço que os métodos built-in do python deixam o código menos eficiente. Correlação ao Match/Case ele está só disponivel na versão >3.11? Ele é mais eficiente do que elif do Python?

1