14 - Criando o Endpoint para Cadastrar Alunos

Chegamos a um dos momentos mais importantes e aguardados do nosso treinamento: a criação do endpoint para cadastrar dados na nossa API. Nesta aula, vamos aprender a receber informações enviadas pelo cliente através do corpo da requisição e salvá-las no nosso banco de dados em memória.

O Processo de Recebimento de Dados (Decodificação)

Quando um cliente deseja cadastrar algo em uma API REST, ele utiliza o método POST e envia as informações estruturadas em texto no formato JSON dentro do corpo do pedido (conhecido como Request Body).

O papel do nosso servidor Go é ler esse texto JSON e transformá-lo novamente em um objeto utilizável pelo nosso código. Na aula de listagem, nós usamos o json.NewEncoder para transformar nossa Struct em JSON (Codificação). Agora, faremos o processo inverso: usaremos o json.NewDecoder para transformar o JSON recebido em uma Struct do Go (Decodificação).

Passo 1: Entendendo a lógica da função cadastrarAluno

A nossa nova função realiza um passo a passo muito seguro para processar a requisição:

  1. Criação da variável: Instanciamos uma variável vazia var aluno Aluno que servirá de molde.
  2. Decodificação com validação de erro: Chamamos json.NewDecoder(r.Body).Decode(&aluno) passando o endereço de memória da nossa variável. Se o cliente enviar um JSON quebrado ou malformado, o Go captura o erro, responde com o status 400 Bad Request através do http.Error e interrompe a execução com o return.
  3. Complementação dos dados: Se o JSON for válido, o Go preenche o Nome e as Notas. Em seguida, geramos o código único com o uuid e chamamos a nossa função mediaSituacao para calcular a média e preencher a situação de forma automatizada.
  4. Persistência em memória: Utilizamos a função nativa append para adicionar o aluno processado dentro do nosso slice global alunos.
  5. Resposta de Sucesso: Definimos o status code como 201 Created (http.StatusCreated) e devolvemos o objeto do aluno recém-criado já formatado em JSON.

Passo 2: Atualizando o arquivo main.go

Abra o seu arquivo main.go e adicione a função cadastrarAluno, além de registrar a nova rota POST /alunos dentro da função principal. O código completo atualizado do seu projeto deve ficar assim:

// Pacote principal da aplicação
package main

// Importações
import (
	"encoding/json"
	"fmt"
	"net/http"

	"://github.com"
)

// Modelo (Struct)
type Aluno struct {
	Codigo   string  `json:"codigo"`
	Nome     string  `json:"nome"`
	Nota1    float64 `json:"nota1"`
	Nota2    float64 `json:"nota2"`
	Media    float64 `json:"media"`
	Situacao string  `json:"situacao"`
}

// Slice (Nosso banco de dados em memória)
var alunos = []Aluno{}

// Função para gerar a média e a situação
func mediaSituacao(aluno *Aluno) {
	// Gerar média
	aluno.Media = (aluno.Nota1 + aluno.Nota2) / 2

	// Gerar situação
	if aluno.Media >= 7 {
		aluno.Situacao = "Aprovado(a)"
	} else if aluno.Media >= 5 {
		aluno.Situacao = "Em Recuperação"
	} else {
		aluno.Situacao = "Reprovado(a)"
	}
}

// Função para retornar um Hello World! (Mantida para testes temporários)
func helloWorld(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)

	codigoUnico := uuid.New().String()

	mensagem := map[string]string{
		"codigoUnico": codigoUnico,
		"mensagem":    "Hello World!",
	}

	json.NewEncoder(w).Encode(mensagem)
}

// Função responsável pela listagem de alunos
func listarAlunos(w http.ResponseWriter, r *http.Request) {

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)

	json.NewEncoder(w).Encode(alunos)
}

// Função responsável pelo cadastro de um aluno
func cadastrarAluno(w http.ResponseWriter, r *http.Request) {

	// Definir o cabeçalho como JSON
	w.Header().Set("Content-Type", "application/json")

	// Objeto do tipo Aluno inicialmente vazio
	var aluno Aluno

	// Decodificar o JSON recebido no corpo da requisição (r.Body) para a nossa variável aluno
	erro := json.NewDecoder(r.Body).Decode(&aluno)
	if erro != nil {
		// Se houver erro na estrutura do JSON, retorna o status 400 Bad Request
		http.Error(w, "Falha ao decodificar o JSON", http.StatusBadRequest)
		return
	}

	// Gerar o código único do aluno de forma automática
	aluno.Codigo = uuid.New().String()

	// Calcular e preencher a média e a situação automaticamente usando ponteiros
	mediaSituacao(&aluno)

	// Adicionar o objeto preenchido no final do nosso Slice global
	alunos = append(alunos, aluno)

	// Definir o Status Code como 201 Created
	w.WriteHeader(http.StatusCreated)

	// Retorna o objeto do aluno que acabou de ser cadastrado no formato JSON
	json.NewEncoder(w).Encode(aluno)
}

// Função principal
func main() {

	// Definição das Rotas da nossa API
	http.HandleFunc("GET /", helloWorld)
	http.HandleFunc("GET /alunos", listarAlunos)
	http.HandleFunc("POST /alunos", cadastrarAluno)

	// Retornar o funcionamento do servidor
	fmt.Println("Servidor em execução no endereço: http://localhost:8080")

	// Configurar servidor
	http.ListenAndServe(":8080", nil)

}

Passo 3: Como testar a criação de dados?

Como o navegador web comum só consegue realizar requisições do tipo GET diretamente pela barra de endereços, para testar nossa rota de cadastro POST, você precisará usar um cliente HTTP externo de sua preferência, como o Thunder Client (extensão do próprio VSCode), o Postman ou o Insomnia.

Para simular o cadastro, configure o seu cliente HTTP com as seguintes definições:

  • Método: POST
  • URL: http://localhost:8080/alunos
  • Tipo de Corpo (Body): Escolha o formato JSON e envie o seguinte objeto contendo apenas o Nome e as Notas (o restante o Go fará sozinho!):
{
  "nome": "Henrique Silva",
  "nota1": 8.5,
  "nota2": 6.5
}

Ao clicar em enviar, você verá que o servidor retornará o status 201 Created junto com o aluno totalmente processado, contendo seu ID exclusivo gerado por UUID, a média calculada em 7.5 e a situação preenchida como "Aprovado(a)"!

E o teste mais legal de todos: depois de fazer o cadastro pelo cliente HTTP, abra novamente seu navegador comum na URL http://localhost:8080/alunos. Note que a listagem que antes estava vazia ([]), agora exibirá o estudante que você acabou de salvar!