17 - Criando o Endpoint para Remover Alunos (DELETE)

Chegamos à aula que fecha o ciclo básico de operações da nossa API (o famoso CRUD: Criar, Ler, Atualizar e Deletar). Nesta aula, vamos implementar o método DELETE para remover um estudante específico da nossa lista utilizando uma técnica muito inteligente de corte de fatias no Go.

A Lógica de Remoção em Slices (Fatiamento)

Diferente de listas em outras linguagens que possuem métodos nativos como remove() ou delete(), o Go nos dá controle total da memória e exige que façamos isso através de um conceito chamado re-fatiamento (reslicing).

Para apagar um elemento em um índice específico do nosso slice, nós usamos a função append para juntar duas partes da nossa lista antiga: tudo o que vem antes daquele índice (alunos[:indice]) com tudo o que vem depois daquele índice (alunos[indice+1:]). O operador de três pontos (...) serve para expandir os elementos da segunda metade. Visualmente, nós simplesmente "pulamos" o registro que queríamos apagar e colamos o restante de volta!

O Status Code 204 (No Content)

Seguindo rigorosamente as boas práticas de desenvolvimento de APIs REST, quando realizamos uma exclusão com sucesso e não temos nenhum dado para devolver ao cliente (já que o recurso foi destruído), o servidor deve responder com o código de status 204 No Content (através da constante http.StatusNoContent). Ele avisa ao cliente: "A sua ordem foi executada com sucesso, mas não há mais nada para exibir aqui".

Passo 1: Atualizando o arquivo main.go

Abra o seu arquivo main.go e insira a nova função removerAluno, mapeando a rota DELETE /alunos/{codigo} na função principal. Não se esqueça de fechar corretamente a chave e o parêntese da função ListenAndServe na linha final. 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) {

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

	codigo := r.PathValue("codigo")

	for indice := range alunos {

		if alunos[indice].Codigo == codigo {

			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 = codigo
			mediaSituacao(&aluno)
			alunos[indice] = aluno

			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(aluno)
			return
		}
	}

	http.Error(w, "Código não encontrado", http.StatusNotFound)
}

// Função responsável pela remoção de um aluno
func removerAluno(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 {

			// Remove o aluno juntando as fatias de memória anteriores e posteriores à dele
			alunos = append(alunos[:indice], alunos[indice+1:]...)

			// Definir o Status Code como 204 No Content (Sucesso sem retorno de corpo)
			w.WriteHeader(http.StatusNoContent)

			// Finaliza imediatamente a ação de remoção
			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)
	http.HandleFunc("DELETE /alunos/{codigo}", removerAluno) // Nova rota de exclusão

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

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

Passo 2: Testando a Exclusão de Dados

Abra o seu cliente HTTP (Postman ou Thunder Client) para fazer a validação completa:

  1. Cadastre um aluno novo usando o método POST e copie o campo codigo que retornou na resposta.
  2. Consulte a listagem no navegador ou via GET /alunos para ter certeza de que ele está salvo.
  3. No seu cliente HTTP, altere o método para DELETE e configure a URL colando o código no final (ex: http://localhost:8080/alunos/COLE_O_CODIGO_AQUI). Clique em enviar.

O programa retornará a resposta com o status 204 No Content e nenhuma mensagem no corpo. Se você voltar na rota de listagem (GET /alunos), verá que os colchetes estarão novamente vazios ([]), provando que o registro foi excluído do nosso banco de dados em memória com sucesso!