16 - Criando o Endpoint para Alterar Dados (PUT)

Agora que nossa API já consegue cadastrar, listar e gerenciar as respostas com o charset correto, precisamos aprender a atualizar os dados de um aluno que já existe no sistema. Nesta aula, usaremos o método PUT e descobriremos como capturar informações passadas diretamente na URL.

Parâmetros de Rota e a Ferramenta PathValue

Para alterar um estudante específico, o cliente precisa nos dizer quem ele quer modificar. O padrão do mercado para isso é passar o identificador único diretamente na URL do endpoint, ficando algo como: /alunos/a7b2c891-....

No roteador nativo das versões modernas do Go, definimos essa variável dinamicamente usando chaves na rota: "PUT /alunos/{codigo}". Para recuperar esse valor de dentro do nosso código de forma extremamente simples, utilizamos o método r.PathValue("codigo").

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

A função de alteração executa um algoritmo muito seguro baseado nos passos abaixo:

  1. Extração do parâmetro: Pegamos o ID enviado na URL usando r.PathValue("codigo").
  2. Varredura no Banco de Dados (Laço For): Percorremos o nosso slice global alunos procurando o registro correspondente.
  3. Decodificação e Atualização: Quando encontramos o código correto, lemos o novo corpo JSON enviado pelo cliente. Mantemos o mesmo código antigo, chamamos a nossa função mediaSituacao para recalcular as notas automaticamente e substituímos o objeto antigo pelo novo diretamente no índice correspondente do slice.
  4. Tratamento de Registro Inexistente: Se o laço de repetição terminar e nenhum código idêntico tiver sido encontrado, o código sai do for e dispara um erro 404 Not Found (http.StatusNotFound) avisando que o estudante não existe.

Passo 2: Atualizando o arquivo main.go

Abra o seu arquivo main.go e insira a nova função alterarAluno, mapeando a rota PUT /alunos/{codigo} na função principal do servidor. O código completo do projeto deve ficar estruturado assim:

// Pacote principal da aplicação
package main

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

	"github.com/google/uuid"
)

// 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; charset=utf-8")
	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) {

	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	var aluno Aluno

	erro := json.NewDecoder(r.Body).Decode(&aluno)
	if erro != nil {
		http.Error(w, "Falha ao decodificar o JSON", http.StatusBadRequest)
		return
	}

	aluno.Codigo = uuid.New().String()
	mediaSituacao(&aluno)
	alunos = append(alunos, aluno)

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(aluno)
}

// Função responsável pela alteração de dados de um aluno
func alterarAluno(w http.ResponseWriter, r *http.Request) {

	// Definir o cabeçalho explicitando o charset UTF-8
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	// Extrair o código dinâmico enviado na URL da requisição
	codigo := r.PathValue("codigo")

	// Laço de repetição para procurar o aluno no nosso slice global
	for indice := range alunos {

		// Condicional para verificar o código de cada aluno existente
		if alunos[indice].Codigo == codigo {

			// Cria uma instância vazia de aluno para receber as novas modificações
			var aluno Aluno

			// Decodificar JSON recebido com os novos dados
			erro := json.NewDecoder(r.Body).Decode(&aluno)
			if erro != nil {
				http.Error(w, "Falha ao decodificar o JSON", http.StatusBadRequest)
				return
			}

			// Garante que o código original do aluno seja preservado e injetado na nova instância
			aluno.Codigo = codigo

			// Recalcular a média e a situação com base nas novas notas enviadas
			mediaSituacao(&aluno)

			// Substitui o aluno antigo pelos novos dados no índice correto do Slice
			alunos[indice] = aluno

			// Definir o Status Code como 200 OK para atualizações bem-sucedidas
			w.WriteHeader(http.StatusOK)

			// Retorna o aluno modificado com os dados atualizados
			json.NewEncoder(w).Encode(aluno)

			// Finaliza imediatamente a ação de alteração para poupar processamento
			return
		}
	}

	// Caso o laço termine sem encontrar o ID informado, retorna erro 404
	http.Error(w, "Código não encontrado", http.StatusNotFound)
}

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

	// Definição das rotas da API
	http.HandleFunc("GET /", helloWorld)
	http.HandleFunc("GET /alunos", listarAlunos)
	http.HandleFunc("POST /alunos", cadastrarAluno)
	http.HandleFunc("PUT /alunos/{codigo}", alterarAluno) // Nova rota com parâmetro dinâmico

	// 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: Testando a Alteração no seu Cliente HTTP

Para testar esta nova rota, siga estes passos ordenados dentro do seu testador HTTP (como Thunder Client):

  1. Primeiro, faça um cadastro usando a rota POST para colocar um aluno válido na memória. Copie o campo codigo que o servidor gerou para ele.
  2. Mude o método para PUT e altere a URL adicionando o código copiado no final (ex: http://localhost:8080/alunos/COLE_O_CODIGO_AQUI).
  3. No corpo (Body) da requisição, modifique o nome ou altere os valores das notas (ex: mude de 5.0 para 9.0) e clique em enviar.

O servidor retornará o status 200 OK e os dados totalmente atualizados, exibindo a nova média e o status recalculado para "Aprovado(a)" instantaneamente!