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.
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!
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".
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)
}
Abra o seu cliente HTTP (Postman ou Thunder Client) para fazer a validação completa:
POST e copie o campo codigo que retornou na resposta.GET /alunos para ter certeza de que ele está salvo.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!