Neste artigo, faremos uma imersão em um serviço básico escrito em Go que pode servir para buscar informações de um CEP (Código de Endereçamento Postal) no Brasil.
Vamos usar a API ViaCep para busca de CEPs brasileiros.
Estrutura do Código
O código é dividido em três partes principais:
A definição do tipo ViaCep, que é uma estrutura (struct) que espelha os dados retornados pela API ViaCep.
A função main, que inicia um servidor HTTP e define o roteamento.
As funções BuscaCepHandle e BuscaCep que lidam com a lógica principal da aplicação.
Definindo o tipo ViaCep
Vamos definir o tipo ViaCep como uma estrutura com vários campos do tipo string. Esses campos refletem as propriedades do objeto JSON que recebemos da API ViaCep.
type ViaCep struct {
Cep string `json:"cep"`
Logradouro string `json:"logradouro"`
Complemento string `json:"complemento"`
Bairro string `json:"bairro"`
Localidade string `json:"localidade"`
Uf string `json:"uf"`
Ibge string `json:"ibge"`
Gia string `json:"gia"`
Ddd string `json:"ddd"`
Siafi string `json:"siafi"`
}
As tags json:"..." anexadas a cada campo da struct fazem o rolê de instruir o pacote encoding/json sobre como fazer o mapeamento do JSON para a struct em Go.
Função Main
Na função main começamos escutando na porta 8080
e configuramos um roteamento de URL simples, onde todas as requisições para a rota /
são tratadas pela função BuscaCepHandle
.
func main() {
http.HandleFunc("/", BuscaCepHandle)
err := http.ListenAndServe(":8080", nil)
if err != nil {
return
}
}
Se um erro ocorrer durante http.ListenAndServe
, nós simplesmente retornamos e encerramos a execução do programa.
Funções BuscaCepHandle e BuscaCep
A função BuscaCepHandle
é nossa manipuladora de requisições HTTP. Ela lida com todas as solicitações que chegam à raíz ("/") da nossa aplicação.
func BuscaCepHandle(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
return
}
cepParam := r.URL.Query().Get("cep")
if cepParam == "" || len(cepParam) != 8 {
w.WriteHeader(http.StatusBadRequest)
return
}
// ... (código truncado por brevidade)
}
Esta função verifica se o parâmetro do cep foi passado na requisição e se ele possui 8 caracteres. Se o CEP não for válido, devolvemos um status HTTP 400.
A função BuscaCep
é uma função auxiliar usada por BuscaCepHandle
para fazer a solicitação real para a API ViaCep e processar os dados de resposta.
func BuscaCep(cep string) (*ViaCep, error) {
url := "https://viacep.com.br/ws/" + cep + "/json/"
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var viaCep ViaCep
err = json.Unmarshal(body, &viaCep)
if err != nil {
return nil, err
}
return &viaCep, nil
}
Esta função faz uma requisição para a API ViaCep, lê a resposta e a deserializa em um objeto ViaCep.
A função retorna um ponteiro para um objeto ViaCep ou um erro, caso algo dê errado durante a solicitação HTTP ou a deserialização do JSON da resposta.
Abaixo eu deixo um exemplo do código completo para você analisar:
package main
import (
"encoding/json"
"io"
"net/http"
)
type ViaCep struct {
Cep string `json:"cep"`
Logradouro string `json:"logradouro"`
Complemento string `json:"complemento"`
Bairro string `json:"bairro"`
Localidade string `json:"localidade"`
Uf string `json:"uf"`
Ibge string `json:"ibge"`
Gia string `json:"gia"`
Ddd string `json:"ddd"`
Siafi string `json:"siafi"`
}
func main() {
http.HandleFunc("/", BuscaCepHandle)
err := http.ListenAndServe(":8080", nil)
if err != nil {
return
}
}
func BuscaCepHandle(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
return
}
cepParam := r.URL.Query().Get("cep")
if cepParam == "" || len(cepParam) != 8 {
w.WriteHeader(http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
viaCep, err := BuscaCep(cepParam)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
err = json.NewEncoder(w).Encode(viaCep)
if err != nil {
return
}
}
func BuscaCep(cep string) (*ViaCep, error) {
url := "https://viacep.com.br/ws/" + cep + "/json/"
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var viaCep ViaCep
err = json.Unmarshal(body, &viaCep)
if err != nil {
return nil, err
}
return &viaCep, nil
}
Pronto!
Isso é o suficiente para criar um microsserviço simples de busca de CEP usando Go.
Se você quiser ter acesso ao código completo, pode acessar o repositório do projeto no GitHub.
Embora seja uma aplicação bastante simples, ela forma a base de como um serviço da vida real pode ser construído utilizando Go para buscar e fornecer informações do CEP a partir de uma API.
Melhorias
Apesar de este ser um código que funciona e retorna o dado que esperamos, existem algumas coisas que podem ser melhoradas neste código.
Tratamento de erros: Quando um erro ocorrer, pode ser interessante retornar a mensagem de erro real ao invés de apenas um código de status HTTP para ajudar na depuração.
Lembre-se de que esta não é uma boa prática para um ambiente de produção, pois pode expor detalhes do sistema a usuários indesejados.
Em um ambiente de produção, uma coisa que você pode fazer é logar o erro e retornar um ID de log para o cliente como uma referência para ele usar na abertura de um atendimento de suporte, por exemplo.
Endpoint do servidor: Atualmente o serviço está configurado para escutar todas as requisições na raiz ("/").
Poderia ser mais inteligente e útil ter um endpoint específico para essa funcionalidade como "/ceps" ou "/buscarCep". O exemplo não considerou isso justamente por ser um exemplo!
Validação do CEP: Poderia ser interessante adicionar uma validação mais forte para o campo 'cep'.
Atualmente, o código apenas verifica se tem 8 caracteres, mas pode existir um cenário onde a entrada tenha 8 caracteres mas não seja um CEP válido. 🤡
Testes: Incluir testes de unidade e integração para garantir que o serviço está funcionando conforme esperado.
Na verdade tudo isso deveria ter começado a partir da escrita de testes. My bad
Goroutine no BuscaCep: A função do BuscaCep
que chama um serviço externo poderia considerar utilizar uma goroutine para executar essa tarefa assincronamente.
Verificações HTTP: A resposta da função http.Get(url) pode vir com statusCode não 200
. Seria inteligente fazer essa verificação antes de ler o corpo da resposta.
Agora sim, pronto!
Talvez quando você ler este post, os arquivos no repositório já tenham sido alterados e algumas dessas (ou todas) melhorias implementadas.
E se você tiver sugestões que colaborem com o aprendizado de outras pessoas sobre este mesmo assunto, fique a vontade para fazer um pull-request no GitHub.
Até a próxima 🤘
Top comments (0)