15 - Compreendendo o que é Charset (Codificação de Caracteres)

Quando desenvolvemos aplicações web, lidamos frequentemente com acentos, cedilhas e caracteres especiais da nossa língua (como em "Aprovado(a)" ou "Recuperação"). Nesta aula, vamos entender o conceito de Charset e como configurá-lo na nossa API para garantir que as respostas sejam exibidas perfeitamente em qualquer sistema ou navegador.

O que é Charset?

O termo Charset vem de Character Set (Conjunto de Caracteres). Ele funciona como uma tabela de tradução que diz ao computador como transformar os códigos binários (zeros e uns) salvos no servidor nos caracteres de texto legíveis que vemos na tela.

O padrão mais utilizado e recomendado no mundo inteiro para a web é o UTF-8. Ele consegue representar praticamente todos os caracteres de quase todas as línguas escritas do planeta, ocupando pouco espaço de memória.

Por que devemos declarar o Charset explicitamente?

Se nós não informarmos ao cliente (navegador ou aplicativo mobile) qual codificação estamos usando, ele tentará adivinhar ou usará o padrão do sistema operacional dele. Isso pode fazer com que palavras acentuadas retornem quebradas, exibindo aqueles símbolos estranhos e ilegíveis (como Recuperao em vez de Recuperação).

Para resolver isso de forma profissional, adicionamos a instrução charset=utf-8 junto à definição do nosso tipo de conteúdo (Content-Type) no cabeçalho da resposta.

Passo 1: Atualizando o arquivo main.go

Abra o seu arquivo main.go e modifique as funções listarAlunos e cadastrarAluno para incluir a especificação de charset no cabeçalho. Veja como o código completo do projeto deve ficar estruturado:

// 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) {

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

	// Definir o Status Code
	w.WriteHeader(http.StatusOK)

	// Retorna uma lista contendo todos os alunos cadastrados
	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 adicionando explicitamente a codificação UTF-8
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	// Objeto do tipo Aluno
	var aluno Aluno

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

	// Gerar o código do aluno
	aluno.Codigo = uuid.New().String()

	// Gerar a média e a situação
	mediaSituacao(&aluno)

	// Cadastrar no Slice
	alunos = append(alunos, aluno)

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

	// Retorna o aluno cadastrado
	json.NewEncoder(w).Encode(aluno)
}

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

	// Rotas
	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 2: Validando o funcionamento

Utilizando o seu cliente HTTP (Postman, Thunder Client ou Insomnia), realize um novo cadastro enviando notas que deixem o aluno com a situação "Em Recuperação". No cabeçalho da resposta (seção Headers), você poderá notar o campo Content-Type preenchido exatamente com application/json; charset=utf-8, garantindo a exibição perfeita dos acentos!