# Como criar um site com Python puro, sem framework (usando CGI)

### O que é CGI?

**CGI (Common Gateway Interface)** é uma especificação padrão que permite a um servidor web transferir dados para e de aplicações externas. Na prática, CGI permite a execução de scripts que geram conteúdo dinâmico em um servidor web.

### Funcionamento

Quando uma requisição atende uma diretiva configurada no servidor, ele cria um processo adicional e passa as informações da requisição via **stdin** para o script configurado. Este script, que pode ser escrito em diversas linguagens como **Python**, processa os dados e gera o conteúdo dinâmico que é devolvido ao servidor e, por fim, ao cliente (navegador).

### Cenário Prático

Imagine que você precisa criar um formulário para coletar dados rapidamente e só tem um servidor virtual com Python disponível. Usando CGI, você pode configurar um servidor web rapidamente sem precisar de frameworks como Flask ou Django.

"Mas Ricardo, por que não usar algo como FastAPI?" Porque você tem pouco tempo para cumprir essa tarefa e não pode criar mais um serviço para o **SRE** da sua empresa monitorar.

Vou pular a parte de configurar a máquina e supor que você já leu o artigo [como configurar uma nova VPS Linux com segurança usando o Ansible](https://blog.ricardotenv.dev/como-reforcar-rapidamente-a-seguranca-da-sua-nova-vps-com-um-playbook-ansible-simples) e já tem acesso seguro à máquina fornecida.

E se você ainda não tem acesso à uma VPS você pode [começar com $200 de créditos usando meu link](https://m.do.co/c/844075225478) da **DigitalOcean**.

### Passos para Implementação

1. **Instale o Apache**
    

```bash
sudo apt-get install apache2
```

Use o comando `python3 --version` para verificar se a máquina já tem o Python instalado, na maioria das distribuições Linux ele já vem instalado por padrão.

2. **Ative o módulo CGI no Apache**:
    

```bash
sudo a2enmod cgi
```

3. **Configure o Apache**:
    

Adicione a seguinte diretiva no arquivo de configuração do **Apache** para permitir a execução de scripts CGI:

```apache
<Directory "/var/www/html/">
    AllowOverride None
    Options +ExecCGI
    AddHandler cgi-script .py
    Require all granted
</Directory>
```

Neste exemplo, qualquer arquivo dentro do diretório /var/www/html/ com a extensão .py será tratado como um script CGI e executado como tal.

4. **Crie e configure um script Python**:
    

```bash
cd /var/www/html
touch hello.py
chmod +x hello.py
```

Conteúdo do `hello.py`:

```python
#!/usr/bin/python3

import os

print("Content-type: text/html\n")
print("<html><head><title>CGI Info</title></head><body>")
print("<h1>CGI Info</h1>")

for key, value in os.environ.items():
    print(f"<p><strong>{key}:</strong> {value}</p>")

print("</body></html>")
```

A simplicidade desse script é notável: o servidor web utiliza o *shebang* para identificar o interpretador a ser usado e passa várias variáveis de ambiente definidas pelo Apache no processo CGI atual. Essas variáveis podem variar com novas solicitações no mesmo endereço, refletindo diferentes dados da requisição. Isso permite que o script CGI responda dinamicamente com base nas informações da requisição.

Agora basta acessar a porta 80 do servidor, por exemplo, [http://146.190.78.111/hello.py](http://146.190.78.111/hello.py) e se você ver algo como a imagem abaixo, é porque já está funcionando.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1717779955640/5d8a40ee-c53c-42fb-91f1-b408650b35b4.png align="center")

Eu poderia encerrar aqui, pois você já aprendeu **como o CGI funciona** e como servir um site em **Python sem um framework web**. No entanto, vamos dar um passo adiante: criar um formulário, coletar os dados e ver tudo funcionando na prática.

### Criação de um Formulário

Crie um formulário HTML que coleta dados e os envia para um script CGI Python que processa esses dados e os salva em um arquivo CSV.

1. **Formulário HTML** `formulario.py`:
    

```python
#!/usr/bin/python3

print("Content-type: text/html\n")
print("""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Formulário CGI</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h1 class="mt-5">Formulário de Sugestões</h1>
        <form method="post" action="/process_form.py">
            <div class="form-group">
                <label for="nome">Nome</label>
                <input type="text" class="form-control" id="nome" name="name" required>
            </div>
            <div class="form-group">
                <label for="sobrenome">Sobrenome</label>
                <input type="text" class="form-control" id="sobrenome" name="surname" required>
            </div>
            <div class="form-group">
                <label for="cargo">Cargo</label>
                <select class="form-control" id="cargo" name="office" required>
                    <option value="Desenvolvedor">Desenvolvedor</option>
                    <option value="Designer">Designer</option>
                    <option value="Gerente">Gerente</option>
                </select>
            </div>
            <div class="form-group">
                <label for="sugestao">Sugestão</label>
                <textarea class="form-control" id="sugestao" name="suggestion" rows="4" required></textarea>
            </div>
            <button type="submit" class="btn btn-primary">Enviar</button>
        </form>
    </div>
</body>
</html>
""")
```

Veja só, que interessante, o que esse script faz é apenas printar um código HTML e o navegador renderiza isso para o usuário como uma página, veja como ficou:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1717844001181/9d20ff59-bc77-459b-a914-fec2f38f05f7.png align="center")

Mas por que isso acontece? O CGI envia os dados do stdout para o servidor web, que os renderiza para o cliente. Quando usamos `print`, estamos enviando dados para o stdout. A primeira linha após o shebang define o tipo de conteúdo que o servidor deve renderizar, como HTML, mas poderia ser JSON, por exemplo.

E vamos para a parte mais interessante, quando esse formulário é preenchido e enviado, ele faz uma requisição POST para outro script, `proccess_form.py` que também será servido como CGI. Vamos criar o script que processará essa requisição, salvando os dados em um arquivo CSV no servidor. Esse script aceitará apenas requisições do tipo POST.

Para começar, importamos as bibliotecas necessárias. Em seguida, configuramos o tipo de conteúdo que será enviado para a saída padrão *stdout* e implementamos uma condição para permitir somente requisições do tipo POST. Caso uma requisição do tipo GET seja feita no script, uma mensagem de erro será retornada.

```python
#!/usr/bin/python3

import csv
import os
import sys

print("Content-type: text/html\n")

if os.environ['REQUEST_METHOD'] == 'POST':
    
else:
    print("<html><body><h1>Método não permitido. Use POST.</h1></body></html>")
```

Agora precisamos recuperar algumas variáveis de ambiente criadas pelo servidor, assim como fizemos com `REQUEST_METHOD`. Vamos usar esse método para acessar o conteúdo do corpo da requisição. Poderíamos usar uma **biblioteca Python** chamada **cgi**, mas neste caso, vamos fazer manualmente para facilitar o entendimento de como isso funciona.

```python
#!/usr/bin/python3

import csv
import os
import sys

print("Content-type: text/html\n")

if os.environ['REQUEST_METHOD'] == 'POST':
    content_length = int(os.environ['CONTENT_LENGTH']
    post_data = sys.stdin.read(content_length)

else:
    print("<html><body><h1>Método não permitido. Use POST.</h1></body></html>")
```

O que estamos fazendo nesse trecho é pegar o comprimento dos dados do corpo da requisição. Como já sabemos, uma variável de ambiente é uma string, então a convertemos para inteiro. Com o comprimento em mãos, pegamos exatamente os dados que queremos no *stdin*. Assim, teremos armazenado em `post_data` algo como `name=Ricardo&surname=Santos&office=Developer&suggestion=Mais+café.`

E então, *parseamos* esses dados para usá-los mais adiante.

```python
#!/usr/bin/python3

import csv
import os
import sys

print("Content-type: text/html\n")

if os.environ['REQUEST_METHOD'] == 'POST':
    content_length = int(os.environ['CONTENT_LENGTH']
    post_data = sys.stdin.read(content_length)
    
    params = post_data.split('&')
    form_data = {}

    for param in params:
        key, value = param.split('=')
        form_data[key] = value

    name = form_data['name']
    surname = form_data['surname']
    office = form_data['office']
    suggestion = form_data['suggestion']
else:
    print("<html><body><h1>Método não permitido. Use POST.</h1></body></html>")
```

E por último vamos salvar os dados desse formulário em uma planilha no formato csv.

```python
#!/usr/bin/python3

import csv
import os
import sys

print("Content-type: text/html\n")

if os.environ['REQUEST_METHOD'] == 'POST':
    content_length = int(os.environ['CONTENT_LENGTH']
    post_data = sys.stdin.read(content_length)
    
    params = post_data.decode('utf-8').split('&')
    form_data = {}

    for param in params:
        key, value = param.split('=')
        form_data[key] = value

    name = form_data['name']
    surname = form_data['surname']
    office = form_data['office']
    suggestion = form_data['suggestion']

     with open('/var/www/html/sugestoes.csv', 'a', newline='') as csvfile:
        fieldnames = ['Nome', 'Sobrenome', 'Cargo', 'Sugestao']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        
        if csvfile.tell() == 0:
            writer.writeheader()
        
        writer.writerow({'Nome': name, 'Sobrenome': surname, 'Cargo': office, 'Sugestao': suggestion})
        print("<html><body><h1>Obrigado pela sua sugestão!</h1></body></html>")
else:
    print("<html><body><h1>Método não permitido. Use POST.</h1></body></html>")
```

Antes de testar, não se esqueça de converter o script em um executável. Se você tiver algum problema de permissões, execute o comando abaixo para conceder essas permissões ao Apache.

```bash
sudo chmod +x /var/www/html/process_form.py
sudo chown -R www-data:www-data /var/www/html/
sudo chmod -R 775 /var/www/html/
```

## Conclusão

O **CGI** foi amplamente utilizado no passado, mas hoje em dia não é recomendado para tarefas complexas. Isso ocorre porque o padrão **CGI** consome muitos recursos, já que um novo processo é aberto para cada requisição. Atualmente, existem métodos mais eficientes para gerar conteúdo dinâmico. Pretendo escrever novas versões deste post para abordar essas formas mais modernas e eficientes.

Embora este método seja bastante simples, ele é essencial para quem deseja aprender e trabalhar com **arquiteturas de micro-serviços**. Compreender os fundamentos do CGI pode fornecer uma base sólida para entender como as requisições e respostas funcionam na web. Além disso, ao dominar esses conceitos básicos, você estará melhor preparado para avançar para tecnologias mais avançadas e eficientes, como **FastCGI**, **WSGI**, ou até mesmo frameworks modernos como Flask e Django, que são amplamente utilizados para desenvolver aplicações web robustas e escaláveis.

Portanto, mesmo que o CGI não seja a melhor escolha para projetos complexos, aprender sobre ele pode ser um passo importante na sua jornada como desenvolvedor.
