bravaweb


Namebravaweb JSON
Version 0.0.21 PyPI version JSON
download
home_pagehttps://github.com/robertons/bravaweb
SummaryBravaWeb Framework for ASGI Server
upload_time2023-12-05 19:03:41
maintainer
docs_urlNone
authorRoberto Neves
requires_python
licenseMIT
keywords asgi wsgi python3 web http framework mysql mariadb
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # BravaWeb Framework for ASGI Server

Framework para aplicações WEB baseada em Python3 ASGI  (_Asynchronous Server Gateway Interfac_  em  Uvicorn), com possibilidade de utilização de Template em Html (Mako Templates).

Veja Documentação em:

Uvicorn: https://www.uvicorn.org/
Mako Templates: https://www.makotemplates.org/

# Instalação
Instalação utilizando Pip
```bash
pip install bravaweb
```
Git/Clone
```
git clone https://github.com/robertons/bravaweb
cd bravaweb
pip install -r requirements.txt
python setup.py install
```

# Primeiros Passos

Inicie seu projeto conforme estrutura abaixo


```bash
app
├── ...
├── configuration                           		
│   ├── __init__.py          
│   └── api.py                   
└── server.py
```

O arquivo de configurações deve conter os seguintes dados:

| variável     		  |    tipo     | obrigatório |  descrição       			        |
|-------------------|-------------|-------------|-------------------------------|
| directory       	| string      | sim         | Caminho Projeto     		     	|
| encoding 		  	  | string      | sim         | Codificação    				        |
| date_format     	| string      | sim         | Formato data             		  |
| short_date_format | string      | sim         | Formato data curta            |
| token 			      | string      | sim         | Token codificação Authorization Header    |
| domains      		  | array       | sim         | Domínios autorizados a acessar|
| access_exceptions | array       | sim         | Rotas e Exceções de acesso   	|
| routes 			      | array       | sim         | Rotas do Projeto      		    |


```python

configuration/__init__.py

# -*- coding: utf-8 -*-

from configuration import api

```


```python

configuration/api.py

# -*- coding: utf-8 -*-

import os

# Directory
directory = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

# Api Encoding
encoding = "utf-8"

# Date Format
date_format = "%d/%m/%Y %H:%M:%S"
short_date_format = "%d/%m/%Y"

# Token Authorization
token = "JWT-Token-Project"

# Authorized Domains Origin/Referrer
domains = [
    "https://www.dominio.com.br",
    "https://alias.dominio.com.br",
]

# Exceptions Routes
access_exceptions = [

    {'path': '(^/default/)','referer': '*'},

    {'path': '(/rota/especifica/)','referer': '(^https://dominio.especifico.com.br/)'},

    {'path': '*', 'referer': "(^https://outro.dominio.com.br/)|(^https://adicional.dominio.com.br/)"},
]

routes = [
    ("{controller}/{area}/{module}/{action}/{id}", '(^/admin/)|(^/panel/)',
    ("{controller}/{module}/{action}/{id}", ""),
]

```

**Definições:**

**domains**:  lista array de strings, com domínios que tem acesso a api, o teste é feito baseado no origin e/ou referrer de cada requisição.

**access_exceptions**: é possivel que algumas rotas sejam abertas para qualquer requisição, ou mesmo que alguma rota seja especifica para algum domínio. A lista deve conter um dicionário com as chaves path e referer onde:

	path: é referente ao caminho da rota
	referer: origem da requisição

Ambos os valores aceitam * para todos ou expressão regular para teste de string.

**routes**:  lista com tuplas que definem as rotas padrões do projeto. Bravaweb esta preparado para até 4 níveis de profundidade que definem, Controlador, Area, Modulo, Ação e mais um nível **opcional** para captação de ID, a prioridade das regras é sequencial, portanto as regras específicas devem vir primeiro. A tupla é definida assim:

	0: a captação de cada parte da profundidade para carregamento
	1: expressão regular para identificar a regra

Por padrão os valores de rota do ambiente são:

    controller = None
    area = None
    module = "default"
    action="index"
    id = None

Por fim vamos  criar a execução do projeto que vai tratar as requisições e processar as rotas.

O arquivo `server.py` na raiz deve ficar assim:

```python
#-*- coding: utf-8 -*-
import sys

import configuration

from bravaweb import App as application

```

Acesse o diretório  do seu projeto, e execute o comando  de serviço do ASGI, conforme documentação do Uvicorn, no exemplo abaixo ativamos o ambiente virtual onde os pacotes estão instalados:


```bash\
source ../env/bin/activate

uvicorn server:application --port 8080 --interface=asgi3 --workers 7 --proxy-headers --lifespan off --reload

```
O Resultado então será:

```bash\
INFO: Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)
INFO: Started reloader process [82503] using statreload
INFO: Started server process [82505]
.
.
.
```

Neste momento sua aplicação estará em execução. Nós configuramos as rotas mas não desenvolvemos nenhuma delas portanto qualquer requisição na url http://127.0.0.1:8080 irá retornar 404.

# Hello World


Vamos iniciar aplicando a rota default, a pasta do projeto nesse momento deverá estar assim:
```bash
app
├── ...
├── configuration                           		
│   ├── __init__.py          
│   └── api.py        
├── controllers
│   └── default.py              
└── server.py
```
Conforme exemplificado a rota default(padrão) é

    controller = None
    area = None
    module = "default"
    action="index"
    id = None


O arquivo ficará assim:

```python

controllers/default.py

# -*- coding: utf-8 -*-
from bravaweb.controller import *

class DefaultController(Controller):

    @get
    async def index(self) -> Json:
        await View(self.enviroment, data={"mensagem": 'Olá Mundo'})

```

**Analisando a rota default:**

Nome do Controlador é default, por isso nome da classe é **Default**Controller, herdando o controlador do framework (Controller)

O metodo de request aceito para esta rota é o GET (*@get*) , mas POST (*@post*) , PUT(*@put*) e DELETE(*@delete*) também são aceitos. Uma requisição diferente do permitido para rota retorna Erro *405: Method not allowed*

O Framwork é baseado em ASGI (_Asynchronous Server Gateway Interface_) por isso ação index é assíncrona (async) .

A anotação é o tipo de resultado que essa rota irá retornar, posteriormente veremos sobre os tipos, no exemplo acima utilizamos Json.

Todos os dados da requisição, estão na *enviroment*, veremos mais logo a seguir.


Para melhor compreenção sobre as rotas , vejamos os exemplos abaixo baseado no arquivo de configuração acima:

# Criando e Configurando Rotas

Os padrões de rota é configurado no arquivo de configurações em routes. Você provavelmente fará isso somente uma vez, ou quando for necessária a criação de rotas específicas em seu projeto.  Abaixo segue alguns exemplos baseado na configuração que apresentamos.

## Exemplo 1

    GET -> api.dominio.com.br/admin/catalog/products/list

A regra identificada é a primeira da lista, pois o path da request inicia com */admin/* conforme expressão regular da posição [1]  da tupla em *configuration.api.routes*:

    ("{controller}/{area}/{module}/{action}/{id}", '(^/admin/)|(^/panel/)'

O resultado da captação da rota conforme posição [0] da tupla será:

    controller = 'admin'
    area = 'catalog'
    module = 'products'
    action = 'list'

A estrutura para processamento desta rota devera ser:

```bash
app
├── ...    
├── controllers
│   └── admin
│   │	└── catalog
│   │	|	└── products.py                
```

O arquivo :

```python

controllers/admin/catalog/products.py

# -*- coding: utf-8 -*-
from bravaweb.controller import *

class ProductsController(Controller):

    @get
    async def list(self) -> Json:
        await View(self.enviroment, data=[{"prod_nome": 'Exemplo'}])

```

## Exemplo 2

    POST -> api.dominio.com.br/site/product/like/110

A regra identificada é a default (segunda da lista), pois o path da request **não** contempla as expressões regulares anteriores :

     ("{controller}/{module}/{action}/{id}", "")

O resultado da captação da rota conforme posição [0] da tupla será:

    controller = 'site'
    area = None
    module = 'product'
    action = 'like'
    id = 110

A estrutura para processamento desta rota devera ser:

```bash
app
├── ...    
├── controllers
│   └── site
│   │	└── product.py                
```

O arquivo:

```python

controllers/site/products.py

# -*- coding: utf-8 -*-
from bravaweb.controller import *

class ProductController(Controller):

    @post
    async def like(self) -> Json:
        await View(self.enviroment, data=[{"likes": 535}])

```

# Ambiente / Enviroment

A qualquer momento dentro do controlador é possivel acessar  os dados da requisição através de *self.enviroment* os dados disponíves são:

|Campo  | Tipo | descrição |
|--|--|--|
| origin | string | Origem ou Referrer da Requisição|
| remote_ip | string |  Ip do usuário|
| remote_uuid | string | UUID se informado no header |
| browser | string | Browser do usuário |
| accept_encoding |  string | tipos de codificação aceito pelo browser |
| method | string | metodo da requisição (GET, POST, PUT ou DELETE) |
| response_type| string | tipo de resposta esperada para requisição|
| authorization| string | Token JWT - Bearer enviado no Header|
| bearer| string | Token JWT decodificado |
| content_length| int | Tamanho da requisição |
| get| dict | Dados enviados por querystring |
| post| dict  | Dados enviados por post |
| body | bytes | Bytes do corpo da requisição |
| route | string | rota |
| controller | string | nome controlador |
| area  |  string | nome area do controlador |
| module  | string |  nome modulo do controlador |
| action  | string | nome da ação do modulo |
| id  | string | identificador da requisição |

Há disponível também, para casos de manipulação específica os dados brutos do ASGI:

|Campo  | descrição |
|--|--|
| headers | cabeçalho da requisição |
| scope | escopo da requisição |
| send | conexão com navegador |
| receive | dados recebidos  |

# Entradas e Pré-condições

Para maior segurança no processamento das rotas é possível e **recomendável** estabelecer as pré-condições daquela rota específica.  Caso a requisição não tenha o objeto ou objeto informado seja inválido, haverá erro de resposta com erro *412: Precondition Failed*

```python
    @post
    async def comment(self, id_product:int, comment:string ) -> Json:
	    sql_query = f"INSERT  INTO products_comments (prod_comment, id_product) VALUES ('{comment}',{id_product})";
	    .
		.
		.
        await View(self.enviroment, data=[{"added": true}])

```

Caso a request não contenha os parametros acima, a ação não será executada.

É possível requerer objetos específicos, Bravaweb realiza o cast automático dos dados enviados, no caso datetime o parametro de conversão esta estabelecido no arquivo de configuração nos campos *date_format* e *short_date_format*.

```python
from datetime import datetime
from decimal import Decimal
.

    @post
    async def comment(self, id_product:int, comment:string, date:datetime, stars:Decimal) -> Json:
	    .
		.
		.
	    .
		.
		.
        await View(self.enviroment, data=[{"added": true}])

```

#  View

Toda rota deve retornar uma view, que será baseada na anotação a action.
```python
        await View(self.enviroment, data=_response_data)
```

Bravaweb possui tratamento específico para respostas Json e HTML, ambos possuem um modelo ou carregamento de  template para resposta.

A View possui os seguintes campos de entrada

| entrada | obrigatório | tipo | descrição
|--|--|--|--|
| enviorment | sim | bravaweb.enviroment | ambiente da requisição
| data | sim | bytes-like, dict, list, string |  dados da resposta de acordo com anotação
| success | não | boolean | sucesso na execução da action
| token | não | string | auth token, caso não informado, havendo token no enviroment, o mesmo se repetirá
| task | não | dict, list, string | dados sobre execução em segundo plano
| error | não | dict, list, string | mensagem de erro


# Anotações e Tipos de Resposta


|tipo  | Entrada |
|--|--|
| Html | dict |
| Css | bytes-like object |
| Csv | bytes-like object |
| JavaScript | bytes-like object |
| Jpg | bytes-like object |
| Json | dict, list, string |
| Mp4 |bytes-like object  |
| Pdf | bytes-like object |
| Png |  bytes-like object|
| TextPlain | bytes-like object |
| Xml | bytes-like object |


# Json
O template Json é composto da seguinte forma:

Json = {
	    "token": "",
	    "success": True,
	    "date": "",
	    "itens": 0,
	    "data": [],
}

Onde os dados respondidos estarão dentro de "data".
```python
    @get
    async def index(self) -> Json:
        await View(self.enviroment, data=[{"added": true}])
```


#  HTML e Template Mako

Para mais informações sobre a criação de templates Mako acesse: https://www.makotemplates.org/

A estrutura das Views HTML desenvolvidas em Mako devem estar assim:

```bash
app
├── ...
├── configuration                           		  
├── controllers
├── views
│   └── shared   
│   |	└── default.html              
└── server.py
```

Quando não há uma view definida para rota, o template padrão a ser carregado será o default.

é possível  criar views específicas para cada rota conforme exemplo abaixo:

Rota: **/product/detail**

```python
    @get
    async def index(self) -> Html:
        await View(self.enviroment, data=[{"added": true}])
```

**Template:**

```bash
app
├── ...
├── configuration                           		  
├── controllers
├── views
│   └── product   
│   |	└── detail
│   |	|	└── index.html  
│   └── shared                
└── server.py
```


# Decoradores

Bravaweb é compatível com encapsulamento através de decorador e a criação deve seguir o modelo abaixo:

**Decorador de Método Síncrono:**

```python
def decorator_example(f):
    def example_decorator(cls, **args) -> f:
        return f(cls, **args)
    return example_decorator
```

**Decorador de Método Assíncrono:**

```python
def decorator_example_async(f):
    async def example_decorator(cls, **args) -> f:
        return await f(cls, **args)
    return example_decorator
```

**O uso do decorador em um método síncrono  ficaria assim:**

```python
    @decorator_example
    def __init__(self):
        .
        .
```

**O uso do decorador em uma rota ficaria assim:**

```python
    @decorator_example_async
    async def index(self) -> Html:
        await View(self.enviroment, data=_response_data)
```


**É possível também criar decorar para um controlador inteiro, a função "decora" todos os métodos executáveis, observe que os métodos padrões de classe  __init__ e __del__ são métodos síncronos e por isso o decorador síncrono, e demais métodos (actions) com decorador assíncrono.**

```python
def decorator_example_klass():
    def decorate(cls):
        for attr in cls.__dict__:
            _method = getattr(cls, attr)
            if hasattr(_method, '__call__'):
                if attr == "__init__" or attr == "__del__":
                    setattr(cls, attr, example_decorator(_method))
                else:
                    setattr(cls, attr, decorator_example_async(_method))
        return cls
    return decorate
```

# Erros:

A qualquer momento no processamento da sua rota é possível responder  com as seguintes mensagens de erro:

| Metoto | Código de Resposta  | Mensagem |
|--|--|--|
| NoContent | 204  | 204: No Content
| Unauthorized | 401  | 401: Unauthorized
| NotFound | 404  | 404: Not Found
| NotAllowed | 405  | 405: Method not allowed
| PreconditionFailed | 412  | 412: Precondition Failed
| InternalError | 500  | 500: Internal Error

**Exemplo requisição de um arquivo pdf:**

```python

import os.path

    @get
    async def index(self, file_path:str) -> Pdf:
	    if os.path.exists(file_path):
			_file_data = open(file_path,'r')
	        await View(self.enviroment, data = _file_data.read())
	    else:
		    self.NotFound()
```


## License

MIT



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/robertons/bravaweb",
    "name": "bravaweb",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "asgi,wsgi,python3,web,http,framework,mysql,mariadb",
    "author": "Roberto Neves",
    "author_email": "robertonsilva@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/73/77/a3b21b43967b2a3466bb74309f81af58c2d6386c42fc2c76239fd1a8b095/bravaweb-0.0.21.tar.gz",
    "platform": null,
    "description": "# BravaWeb Framework for ASGI Server\n\nFramework para aplica\u00e7\u00f5es WEB baseada em Python3 ASGI  (_Asynchronous Server Gateway Interfac_  em  Uvicorn), com possibilidade de utiliza\u00e7\u00e3o de Template em Html (Mako Templates).\n\nVeja Documenta\u00e7\u00e3o em:\n\nUvicorn: https://www.uvicorn.org/\nMako Templates: https://www.makotemplates.org/\n\n# Instala\u00e7\u00e3o\nInstala\u00e7\u00e3o utilizando Pip\n```bash\npip install bravaweb\n```\nGit/Clone\n```\ngit clone https://github.com/robertons/bravaweb\ncd bravaweb\npip install -r requirements.txt\npython setup.py install\n```\n\n# Primeiros Passos\n\nInicie seu projeto conforme estrutura abaixo\n\n\n```bash\napp\n\u251c\u2500\u2500 ...\n\u251c\u2500\u2500 configuration                           \t\t\n\u2502   \u251c\u2500\u2500 __init__.py          \n\u2502   \u2514\u2500\u2500 api.py                   \n\u2514\u2500\u2500 server.py\n```\n\nO arquivo de configura\u00e7\u00f5es deve conter os seguintes dados:\n\n| vari\u00e1vel     \t\t  |    tipo     | obrigat\u00f3rio |  descri\u00e7\u00e3o       \t\t\t        |\n|-------------------|-------------|-------------|-------------------------------|\n| directory       \t| string      | sim         | Caminho Projeto     \t\t     \t|\n| encoding \t\t  \t  | string      | sim         | Codifica\u00e7\u00e3o    \t\t\t\t        |\n| date_format     \t| string      | sim         | Formato data             \t\t  |\n| short_date_format | string      | sim         | Formato data curta            |\n| token \t\t\t      | string      | sim         | Token codifica\u00e7\u00e3o Authorization Header    |\n| domains      \t\t  | array       | sim         | Dom\u00ednios autorizados a acessar|\n| access_exceptions | array       | sim         | Rotas e Exce\u00e7\u00f5es de acesso   \t|\n| routes \t\t\t      | array       | sim         | Rotas do Projeto      \t\t    |\n\n\n```python\n\nconfiguration/__init__.py\n\n# -*- coding: utf-8 -*-\n\nfrom configuration import api\n\n```\n\n\n```python\n\nconfiguration/api.py\n\n# -*- coding: utf-8 -*-\n\nimport os\n\n# Directory\ndirectory = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))\n\n# Api Encoding\nencoding = \"utf-8\"\n\n# Date Format\ndate_format = \"%d/%m/%Y %H:%M:%S\"\nshort_date_format = \"%d/%m/%Y\"\n\n# Token Authorization\ntoken = \"JWT-Token-Project\"\n\n# Authorized Domains Origin/Referrer\ndomains = [\n    \"https://www.dominio.com.br\",\n    \"https://alias.dominio.com.br\",\n]\n\n# Exceptions Routes\naccess_exceptions = [\n\n    {'path': '(^/default/)','referer': '*'},\n\n    {'path': '(/rota/especifica/)','referer': '(^https://dominio.especifico.com.br/)'},\n\n    {'path': '*', 'referer': \"(^https://outro.dominio.com.br/)|(^https://adicional.dominio.com.br/)\"},\n]\n\nroutes = [\n    (\"{controller}/{area}/{module}/{action}/{id}\", '(^/admin/)|(^/panel/)',\n    (\"{controller}/{module}/{action}/{id}\", \"\"),\n]\n\n```\n\n**Defini\u00e7\u00f5es:**\n\n**domains**:  lista array de strings, com dom\u00ednios que tem acesso a api, o teste \u00e9 feito baseado no origin e/ou referrer de cada requisi\u00e7\u00e3o.\n\n**access_exceptions**: \u00e9 possivel que algumas rotas sejam abertas para qualquer requisi\u00e7\u00e3o, ou mesmo que alguma rota seja especifica para algum dom\u00ednio. A lista deve conter um dicion\u00e1rio com as chaves path e referer onde:\n\n\tpath: \u00e9 referente ao caminho da rota\n\treferer: origem da requisi\u00e7\u00e3o\n\nAmbos os valores aceitam * para todos ou express\u00e3o regular para teste de string.\n\n**routes**:  lista com tuplas que definem as rotas padr\u00f5es do projeto. Bravaweb esta preparado para at\u00e9 4 n\u00edveis de profundidade que definem, Controlador, Area, Modulo, A\u00e7\u00e3o e mais um n\u00edvel **opcional** para capta\u00e7\u00e3o de ID, a prioridade das regras \u00e9 sequencial, portanto as regras espec\u00edficas devem vir primeiro. A tupla \u00e9 definida assim:\n\n\t0: a capta\u00e7\u00e3o de cada parte da profundidade para carregamento\n\t1: express\u00e3o regular para identificar a regra\n\nPor padr\u00e3o os valores de rota do ambiente s\u00e3o:\n\n    controller = None\n    area = None\n    module = \"default\"\n    action=\"index\"\n    id = None\n\nPor fim vamos  criar a execu\u00e7\u00e3o do projeto que vai tratar as requisi\u00e7\u00f5es e processar as rotas.\n\nO arquivo `server.py` na raiz deve ficar assim:\n\n```python\n#-*- coding: utf-8 -*-\nimport sys\n\nimport configuration\n\nfrom bravaweb import App as application\n\n```\n\nAcesse o diret\u00f3rio  do seu projeto, e execute o comando  de servi\u00e7o do ASGI, conforme documenta\u00e7\u00e3o do Uvicorn, no exemplo abaixo ativamos o ambiente virtual onde os pacotes est\u00e3o instalados:\n\n\n```bash\\\nsource ../env/bin/activate\n\nuvicorn server:application --port 8080 --interface=asgi3 --workers 7 --proxy-headers --lifespan off --reload\n\n```\nO Resultado ent\u00e3o ser\u00e1:\n\n```bash\\\nINFO: Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)\nINFO: Started reloader process [82503] using statreload\nINFO: Started server process [82505]\n.\n.\n.\n```\n\nNeste momento sua aplica\u00e7\u00e3o estar\u00e1 em execu\u00e7\u00e3o. N\u00f3s configuramos as rotas mas n\u00e3o desenvolvemos nenhuma delas portanto qualquer requisi\u00e7\u00e3o na url http://127.0.0.1:8080 ir\u00e1 retornar 404.\n\n# Hello World\n\n\nVamos iniciar aplicando a rota default, a pasta do projeto nesse momento dever\u00e1 estar assim:\n```bash\napp\n\u251c\u2500\u2500 ...\n\u251c\u2500\u2500 configuration                           \t\t\n\u2502   \u251c\u2500\u2500 __init__.py          \n\u2502   \u2514\u2500\u2500 api.py        \n\u251c\u2500\u2500 controllers\n\u2502   \u2514\u2500\u2500 default.py              \n\u2514\u2500\u2500 server.py\n```\nConforme exemplificado a rota default(padr\u00e3o) \u00e9\n\n    controller = None\n    area = None\n    module = \"default\"\n    action=\"index\"\n    id = None\n\n\nO arquivo ficar\u00e1 assim:\n\n```python\n\ncontrollers/default.py\n\n# -*- coding: utf-8 -*-\nfrom bravaweb.controller import *\n\nclass DefaultController(Controller):\n\n    @get\n    async def index(self) -> Json:\n        await View(self.enviroment, data={\"mensagem\": 'Ol\u00e1 Mundo'})\n\n```\n\n**Analisando a rota default:**\n\nNome do Controlador \u00e9 default, por isso nome da classe \u00e9 **Default**Controller, herdando o controlador do framework (Controller)\n\nO metodo de request aceito para esta rota \u00e9 o GET (*@get*) , mas POST (*@post*) , PUT(*@put*) e DELETE(*@delete*) tamb\u00e9m s\u00e3o aceitos. Uma requisi\u00e7\u00e3o diferente do permitido para rota retorna Erro *405: Method not allowed*\n\nO Framwork \u00e9 baseado em ASGI (_Asynchronous Server Gateway Interface_) por isso a\u00e7\u00e3o index \u00e9 ass\u00edncrona (async) .\n\nA anota\u00e7\u00e3o \u00e9 o tipo de resultado que essa rota ir\u00e1 retornar, posteriormente veremos sobre os tipos, no exemplo acima utilizamos Json.\n\nTodos os dados da requisi\u00e7\u00e3o, est\u00e3o na *enviroment*, veremos mais logo a seguir.\n\n\nPara melhor compreen\u00e7\u00e3o sobre as rotas , vejamos os exemplos abaixo baseado no arquivo de configura\u00e7\u00e3o acima:\n\n# Criando e Configurando Rotas\n\nOs padr\u00f5es de rota \u00e9 configurado no arquivo de configura\u00e7\u00f5es em routes. Voc\u00ea provavelmente far\u00e1 isso somente uma vez, ou quando for necess\u00e1ria a cria\u00e7\u00e3o de rotas espec\u00edficas em seu projeto.  Abaixo segue alguns exemplos baseado na configura\u00e7\u00e3o que apresentamos.\n\n## Exemplo 1\n\n    GET -> api.dominio.com.br/admin/catalog/products/list\n\nA regra identificada \u00e9 a primeira da lista, pois o path da request inicia com */admin/* conforme express\u00e3o regular da posi\u00e7\u00e3o [1]  da tupla em *configuration.api.routes*:\n\n    (\"{controller}/{area}/{module}/{action}/{id}\", '(^/admin/)|(^/panel/)'\n\nO resultado da capta\u00e7\u00e3o da rota conforme posi\u00e7\u00e3o [0] da tupla ser\u00e1:\n\n    controller = 'admin'\n    area = 'catalog'\n    module = 'products'\n    action = 'list'\n\nA estrutura para processamento desta rota devera ser:\n\n```bash\napp\n\u251c\u2500\u2500 ...    \n\u251c\u2500\u2500 controllers\n\u2502   \u2514\u2500\u2500 admin\n\u2502   \u2502\t\u2514\u2500\u2500 catalog\n\u2502   \u2502\t|\t\u2514\u2500\u2500 products.py                \n```\n\nO arquivo :\n\n```python\n\ncontrollers/admin/catalog/products.py\n\n# -*- coding: utf-8 -*-\nfrom bravaweb.controller import *\n\nclass ProductsController(Controller):\n\n    @get\n    async def list(self) -> Json:\n        await View(self.enviroment, data=[{\"prod_nome\": 'Exemplo'}])\n\n```\n\n## Exemplo 2\n\n    POST -> api.dominio.com.br/site/product/like/110\n\nA regra identificada \u00e9 a default (segunda da lista), pois o path da request **n\u00e3o** contempla as express\u00f5es regulares anteriores :\n\n     (\"{controller}/{module}/{action}/{id}\", \"\")\n\nO resultado da capta\u00e7\u00e3o da rota conforme posi\u00e7\u00e3o [0] da tupla ser\u00e1:\n\n    controller = 'site'\n    area = None\n    module = 'product'\n    action = 'like'\n    id = 110\n\nA estrutura para processamento desta rota devera ser:\n\n```bash\napp\n\u251c\u2500\u2500 ...    \n\u251c\u2500\u2500 controllers\n\u2502   \u2514\u2500\u2500 site\n\u2502   \u2502\t\u2514\u2500\u2500 product.py                \n```\n\nO arquivo:\n\n```python\n\ncontrollers/site/products.py\n\n# -*- coding: utf-8 -*-\nfrom bravaweb.controller import *\n\nclass ProductController(Controller):\n\n    @post\n    async def like(self) -> Json:\n        await View(self.enviroment, data=[{\"likes\": 535}])\n\n```\n\n# Ambiente / Enviroment\n\nA qualquer momento dentro do controlador \u00e9 possivel acessar  os dados da requisi\u00e7\u00e3o atrav\u00e9s de *self.enviroment* os dados dispon\u00edves s\u00e3o:\n\n|Campo  | Tipo | descri\u00e7\u00e3o |\n|--|--|--|\n| origin | string | Origem ou Referrer da Requisi\u00e7\u00e3o|\n| remote_ip | string |  Ip do usu\u00e1rio|\n| remote_uuid | string | UUID se informado no header |\n| browser | string | Browser do usu\u00e1rio |\n| accept_encoding |  string | tipos de codifica\u00e7\u00e3o aceito pelo browser |\n| method | string | metodo da requisi\u00e7\u00e3o (GET, POST, PUT ou DELETE) |\n| response_type| string | tipo de resposta esperada para requisi\u00e7\u00e3o|\n| authorization| string | Token JWT - Bearer enviado no Header|\n| bearer| string | Token JWT decodificado |\n| content_length| int | Tamanho da requisi\u00e7\u00e3o |\n| get| dict | Dados enviados por querystring |\n| post| dict  | Dados enviados por post |\n| body | bytes | Bytes do corpo da requisi\u00e7\u00e3o |\n| route | string | rota |\n| controller | string | nome controlador |\n| area  |  string | nome area do controlador |\n| module  | string |  nome modulo do controlador |\n| action  | string | nome da a\u00e7\u00e3o do modulo |\n| id  | string | identificador da requisi\u00e7\u00e3o |\n\nH\u00e1 dispon\u00edvel tamb\u00e9m, para casos de manipula\u00e7\u00e3o espec\u00edfica os dados brutos do ASGI:\n\n|Campo  | descri\u00e7\u00e3o |\n|--|--|\n| headers | cabe\u00e7alho da requisi\u00e7\u00e3o |\n| scope | escopo da requisi\u00e7\u00e3o |\n| send | conex\u00e3o com navegador |\n| receive | dados recebidos  |\n\n# Entradas e Pr\u00e9-condi\u00e7\u00f5es\n\nPara maior seguran\u00e7a no processamento das rotas \u00e9 poss\u00edvel e **recomend\u00e1vel** estabelecer as pr\u00e9-condi\u00e7\u00f5es daquela rota espec\u00edfica.  Caso a requisi\u00e7\u00e3o n\u00e3o tenha o objeto ou objeto informado seja inv\u00e1lido, haver\u00e1 erro de resposta com erro *412: Precondition Failed*\n\n```python\n    @post\n    async def comment(self, id_product:int, comment:string ) -> Json:\n\t    sql_query = f\"INSERT  INTO products_comments (prod_comment, id_product) VALUES ('{comment}',{id_product})\";\n\t    .\n\t\t.\n\t\t.\n        await View(self.enviroment, data=[{\"added\": true}])\n\n```\n\nCaso a request n\u00e3o contenha os parametros acima, a a\u00e7\u00e3o n\u00e3o ser\u00e1 executada.\n\n\u00c9 poss\u00edvel requerer objetos espec\u00edficos, Bravaweb realiza o cast autom\u00e1tico dos dados enviados, no caso datetime o parametro de convers\u00e3o esta estabelecido no arquivo de configura\u00e7\u00e3o nos campos *date_format* e *short_date_format*.\n\n```python\nfrom datetime import datetime\nfrom decimal import Decimal\n.\n\n    @post\n    async def comment(self, id_product:int, comment:string, date:datetime, stars:Decimal) -> Json:\n\t    .\n\t\t.\n\t\t.\n\t    .\n\t\t.\n\t\t.\n        await View(self.enviroment, data=[{\"added\": true}])\n\n```\n\n#  View\n\nToda rota deve retornar uma view, que ser\u00e1 baseada na anota\u00e7\u00e3o a action.\n```python\n        await View(self.enviroment, data=_response_data)\n```\n\nBravaweb possui tratamento espec\u00edfico para respostas Json e HTML, ambos possuem um modelo ou carregamento de  template para resposta.\n\nA View possui os seguintes campos de entrada\n\n| entrada | obrigat\u00f3rio | tipo | descri\u00e7\u00e3o\n|--|--|--|--|\n| enviorment | sim | bravaweb.enviroment | ambiente da requisi\u00e7\u00e3o\n| data | sim | bytes-like, dict, list, string |  dados da resposta de acordo com anota\u00e7\u00e3o\n| success | n\u00e3o | boolean | sucesso na execu\u00e7\u00e3o da action\n| token | n\u00e3o | string | auth token, caso n\u00e3o informado, havendo token no enviroment, o mesmo se repetir\u00e1\n| task | n\u00e3o | dict, list, string | dados sobre execu\u00e7\u00e3o em segundo plano\n| error | n\u00e3o | dict, list, string | mensagem de erro\n\n\n# Anota\u00e7\u00f5es e Tipos de Resposta\n\n\n|tipo  | Entrada |\n|--|--|\n| Html | dict |\n| Css | bytes-like object |\n| Csv | bytes-like object |\n| JavaScript | bytes-like object |\n| Jpg | bytes-like object |\n| Json | dict, list, string |\n| Mp4 |bytes-like object  |\n| Pdf | bytes-like object |\n| Png |  bytes-like object|\n| TextPlain | bytes-like object |\n| Xml | bytes-like object |\n\n\n# Json\nO template Json \u00e9 composto da seguinte forma:\n\nJson = {\n\t    \"token\": \"\",\n\t    \"success\": True,\n\t    \"date\": \"\",\n\t    \"itens\": 0,\n\t    \"data\": [],\n}\n\nOnde os dados respondidos estar\u00e3o dentro de \"data\".\n```python\n    @get\n    async def index(self) -> Json:\n        await View(self.enviroment, data=[{\"added\": true}])\n```\n\n\n#  HTML e Template Mako\n\nPara mais informa\u00e7\u00f5es sobre a cria\u00e7\u00e3o de templates Mako acesse: https://www.makotemplates.org/\n\nA estrutura das Views HTML desenvolvidas em Mako devem estar assim:\n\n```bash\napp\n\u251c\u2500\u2500 ...\n\u251c\u2500\u2500 configuration                           \t\t  \n\u251c\u2500\u2500 controllers\n\u251c\u2500\u2500 views\n\u2502   \u2514\u2500\u2500 shared   \n\u2502   |\t\u2514\u2500\u2500 default.html              \n\u2514\u2500\u2500 server.py\n```\n\nQuando n\u00e3o h\u00e1 uma view definida para rota, o template padr\u00e3o a ser carregado ser\u00e1 o default.\n\n\u00e9 poss\u00edvel  criar views espec\u00edficas para cada rota conforme exemplo abaixo:\n\nRota: **/product/detail**\n\n```python\n    @get\n    async def index(self) -> Html:\n        await View(self.enviroment, data=[{\"added\": true}])\n```\n\n**Template:**\n\n```bash\napp\n\u251c\u2500\u2500 ...\n\u251c\u2500\u2500 configuration                           \t\t  \n\u251c\u2500\u2500 controllers\n\u251c\u2500\u2500 views\n\u2502   \u2514\u2500\u2500 product   \n\u2502   |\t\u2514\u2500\u2500 detail\n\u2502   |\t|\t\u2514\u2500\u2500 index.html  \n\u2502   \u2514\u2500\u2500 shared                \n\u2514\u2500\u2500 server.py\n```\n\n\n# Decoradores\n\nBravaweb \u00e9 compat\u00edvel com encapsulamento atrav\u00e9s de decorador e a cria\u00e7\u00e3o deve seguir o modelo abaixo:\n\n**Decorador de M\u00e9todo S\u00edncrono:**\n\n```python\ndef decorator_example(f):\n    def example_decorator(cls, **args) -> f:\n        return f(cls, **args)\n    return example_decorator\n```\n\n**Decorador de M\u00e9todo Ass\u00edncrono:**\n\n```python\ndef decorator_example_async(f):\n    async def example_decorator(cls, **args) -> f:\n        return await f(cls, **args)\n    return example_decorator\n```\n\n**O uso do decorador em um m\u00e9todo s\u00edncrono  ficaria assim:**\n\n```python\n    @decorator_example\n    def __init__(self):\n        .\n        .\n```\n\n**O uso do decorador em uma rota ficaria assim:**\n\n```python\n    @decorator_example_async\n    async def index(self) -> Html:\n        await View(self.enviroment, data=_response_data)\n```\n\n\n**\u00c9 poss\u00edvel tamb\u00e9m criar decorar para um controlador inteiro, a fun\u00e7\u00e3o \"decora\" todos os m\u00e9todos execut\u00e1veis, observe que os m\u00e9todos padr\u00f5es de classe  __init__ e __del__ s\u00e3o m\u00e9todos s\u00edncronos e por isso o decorador s\u00edncrono, e demais m\u00e9todos (actions) com decorador ass\u00edncrono.**\n\n```python\ndef decorator_example_klass():\n    def decorate(cls):\n        for attr in cls.__dict__:\n            _method = getattr(cls, attr)\n            if hasattr(_method, '__call__'):\n                if attr == \"__init__\" or attr == \"__del__\":\n                    setattr(cls, attr, example_decorator(_method))\n                else:\n                    setattr(cls, attr, decorator_example_async(_method))\n        return cls\n    return decorate\n```\n\n# Erros:\n\nA qualquer momento no processamento da sua rota \u00e9 poss\u00edvel responder  com as seguintes mensagens de erro:\n\n| Metoto | C\u00f3digo de Resposta  | Mensagem |\n|--|--|--|\n| NoContent | 204  | 204: No Content\n| Unauthorized | 401  | 401: Unauthorized\n| NotFound | 404  | 404: Not Found\n| NotAllowed | 405  | 405: Method not allowed\n| PreconditionFailed | 412  | 412: Precondition Failed\n| InternalError | 500  | 500: Internal Error\n\n**Exemplo requisi\u00e7\u00e3o de um arquivo pdf:**\n\n```python\n\nimport os.path\n\n    @get\n    async def index(self, file_path:str) -> Pdf:\n\t    if os.path.exists(file_path):\n\t\t\t_file_data = open(file_path,'r')\n\t        await View(self.enviroment, data = _file_data.read())\n\t    else:\n\t\t    self.NotFound()\n```\n\n\n## License\n\nMIT\n\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "BravaWeb Framework for ASGI Server",
    "version": "0.0.21",
    "project_urls": {
        "Homepage": "https://github.com/robertons/bravaweb"
    },
    "split_keywords": [
        "asgi",
        "wsgi",
        "python3",
        "web",
        "http",
        "framework",
        "mysql",
        "mariadb"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7377a3b21b43967b2a3466bb74309f81af58c2d6386c42fc2c76239fd1a8b095",
                "md5": "268e0733bb56ed3bce4678f229bbe626",
                "sha256": "d6df005ab8eaac322c873630ba03ca8c4c7f890c7c6f99bc30b85bdc9a419bc8"
            },
            "downloads": -1,
            "filename": "bravaweb-0.0.21.tar.gz",
            "has_sig": false,
            "md5_digest": "268e0733bb56ed3bce4678f229bbe626",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 24456,
            "upload_time": "2023-12-05T19:03:41",
            "upload_time_iso_8601": "2023-12-05T19:03:41.375482Z",
            "url": "https://files.pythonhosted.org/packages/73/77/a3b21b43967b2a3466bb74309f81af58c2d6386c42fc2c76239fd1a8b095/bravaweb-0.0.21.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-05 19:03:41",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "robertons",
    "github_project": "bravaweb",
    "github_not_found": true,
    "lcname": "bravaweb"
}
        
Elapsed time: 0.29550s