Alura > Cursos de Programação > Cursos de GoLang > Conteúdos de GoLang > Primeiras aulas do curso Go: gerenciando e otimizando sua API

Go: gerenciando e otimizando sua API

Deletando e editando recursos - Apresentação

Olá, meu nome é Guilherme Lima e estamos felizes em ter você aqui para nos aprofundar ainda mais no assunto de GoLang (Linguagem de Programação Go).

Audiodescrição: Guilherme Lima se identifica como um homem branco. Possui cabelo curto e barba castanhos, e olhos também castanhos. Usa óculos quadrados de armação preta e está com uma blusa bege com capuz abaixado. Ao fundo, há uma parede lisa com um quadro e um abajur ligado à esquerda. A parede está iluminada em tons de roxo e turquesa.

O que aprenderemos?

Neste curso, vamos finalizar o CRUD e arquitetar a estrutura de pastas com base no que ocorre no cotidiano do mercado. Vamos criar pastas "cmd", "internal", "data", "handler", "model", "service" e estabelecer regras de negócio para a aplicação.

Vamos terminar o curso com a estrutura de pastas abaixo.

Além disso, ao fazer a requisição para um determinado endpoint, conseguiremos visualizar informações do contexto de negócio, como a pizza que estamos cadastrando, a avaliação dessa pizza, validando as notas máxima e mínima, e identificando se é válida ou não.

Há muitas coisas interessantes na segunda parte do curso de Go. Vamos aprofundar ainda mais os nossos conhecimentos com essa linguagem tão incrível e tão amada.

Deletando e editando recursos - Novas rotas

Já carregamos nosso projeto base e gostaríamos de concluir o CRUD que iniciamos no curso anterior. Analisando o código que temos no main, somos capazes de listar todas as pizzas cadastradas, criar uma nova pizza com o método POST, e usar o método GET, através do ID, para visualizar uma pizza. Lembrando que todas essas pizzas estão sendo escritas por nós em um arquivo json.

Vamos colocar o servidor para rodar usando o comando go run no terminal e mostrar isso. Realizando um GET para 8080/pizzas, listamos todas as pizzas que temos no nosso arquivo JSON.

Se colocamos, por exemplo, 8080/pizzas/2, conseguimos visualizar apenas a pizza de atum com queijo. Se fazemos um POST para 8080/pizzas, ele retorna o status de 201, e provavelmente no nosso arquivo temos mais uma pizza.

Vamos visualizar, e temos o ID 3, atum com queijo Rust. Se fazemos uma requisição GET para todas as pizzas, conseguimos visualizar as três.

Excluindo e editando pizzas

Entretanto, gostaríamos de ser capazes de deletar uma pizza, ou seja, precisamos de um endpoint para deletar, e existe uma rota que podemos definir, através do router.

Então, escrevemos router.DELETE dentro das chaves do main. Quem queremos deletar? Precisar informar o ID da pizza que queremos deletar. Então, vamos colocar "/pizzas/:id" e vai existir uma função que será capaz de realizar esse método delete.

Da mesma forma que queremos deletar uma pizza, queremos ser capazes de editar ou atualizar uma pizza. Para isso também, vamos criar uma rota nova, só que o método que vamos usar será o PUT, então, "/pizzas/:id", que vai representar o ID da pizza que queremos editar.

No método DELETE, vamos criar uma função, por exemplo, que vai se chamar deletePizzaById, ou seja, deleta uma pizza pelo ID. E na função de editar, vamos colocar, por exemplo, updatePizzaByID.

func main() {
    loadPizzas()
    router := gin.Default()
    router.GET("/pizzas", getPizzas)
    router.POST("/pizzas", postPizzas)
    router.GET("/pizzas/:id", getPizzasByID)
    router.DELETE("/pizzas/:id", deletePizzaById)
    router.PUT("/pizzas/:id", updatePizzaByID)
    router.Run()
}

Vamos criar essas duas funções? Então, vamos criar a primeira função, func deletePizzaById(), e a segunda função vai se chamar func updatePizzaByID(). O que queremos fazer? Queremos apenas verificar, por exemplo, se estamos caindo nessa função, e depois vamos, de fato, desenvolver cada uma delas.

Para essa função ser chamada, lembre-se que precisamos de uma requisição DELETE, no caso de deletar, ou PUT, no caso de editar. Então, o que vamos fazer? Para conseguirmos visualizar uma informação no Thunderclient, vamos pegar o contexto do gin.

Quando realizarmos uma requisição, queremos que exiba uma mensagem lá, por exemplo, "pizza deletada", mesmo que ela não tenha sido. Então, vamos fazer o seguinte, c *gin.context, e dentro das chaves, vamos escrever c.JSON, ou seja, mostra uma mensagem em JSON.

O status vai ser 200, que deu tudo certo, vamos usar o gin.H, entre chaves vamos passar uma mensagem, "method":"delete". E vamos fazer a mesma coisa para o update, só que não vamos usar o método delete, também precisamos do gin, c *gin.context, e aqui é o método put.

func deletePizzaById(c *gin.Context) {
    c.JSON(200, gin.H{"method": "delete"})
}

func updatePizzaByID(c *gin.Context) {
    c.JSON(200, gin.H{"method": "put"})
}

Temos aqui os nossos dois métodos, vamos parar o nosso servidor e executá-lo de novo, vamos voltar na nossa aplicação. Vamos fazer um GET para visualizar todas as pizzas, temos três pizzas: toscana, atum com queijo e queijo Rust.

Se fazemos um GET no id 1, vemos só a pizza de toscana, se pedimos, por exemplo, um PUT nesse endpoint, ele está chamando corretamente, caímos no método que acabamos de criar. E se pedimos, por exemplo, para ele deletar, selecionando DELETE, ele cai no método delete.

Qual é o nosso próximo desafio? Já configuramos a rota que queríamos, já falamos quais eram as funções handler que vamos utilizar para que essa ação aconteça.

No próximo vídeo, queremos mostrar como fazemos uma requisição com o HTTP, com o verbo DELETE para o id 1 e ele deletar, de fato, a pizza que tenha esse id 1 ou id 2 e assim por diante. Vamos descobrir isso na sequência.

Deletando e editando recursos - Method Delete

Vamos desenvolver a função que deleta as pizzas pelo ID. Primeiramente, recebemos o ID como parâmetro em router.DELETE. Precisamos pegar esse ID, convertê-lo para um valor inteiro e verificar se, dentro da nossa lista de pizzas, temos algum ID que o represente.

Se tivermos, removemos essa pizza da nossa lista. Caso contrário, precisamos informar que não encontramos essa pizza na nossa base de dados. Vamos realizar essas ações passo a passo.

Vamos comentar a linha inteira dentro das chaves de func deletePizzaById, pois a utilizaremos em breve. Acima da linha recém-comentada, escreveremos idParam, que é o parâmetro que vamos utilizar com := c.Param("id").

Com isso, vamos pedir para pegar o parâmetro no contexto que tenha o nome de "id". Em seguida, vamos converter esse ID, que é uma string, em um inteiro. Então, strconv.Atoi(idParam), do Async para inteiro, o parâmetro idParam.

Toda essa tentativa de converter para inteiro vai nos retornar dois valores. O primeiro valor vai ser o ID (id), que teremos convertido, e uma mensagem de erro (err), caso tenhamos algum problema.

Então, vamos escrever if err. Se o erro não for igual a nulo, significa que tivemos algum erro tentando converter esse valor. Então, podemos passar uma mensagem, c.JSON(), informando que temos o código 400, ou seja, tivemos um problema, gin.H, entre chaves, vamos exibir, por exemplo, "erro":, e em seguida exibimos a mensagem de erro, pegando da variável do erro, err.Error, e aqui passamos essa mensagem.

No final, se passamos essa mensagem, significa que não vamos mais continuar essa função. Então, colocamos o return, e a nossa função acaba. Se não tivemos um erro, o erro é igual a nil, não tivemos problema nenhum, o próximo passo seria percorrer todas as pizzas cadastradas.

func deletePizzaById(c *gin.Context) {
    idParam := c.Param("id")
    id, err := strconv.Atoi(idParam)
    if err != nil {
        c.JSON(400, gin.H{
            "erro": err.Error()})
        return
    }

Assim, verificamos se o ID que recebemos é igual a 1, a 2 ou a 3. Então, para isso vamos ter uma ação um pouco diferente do que aquela que executamos para pegar a pizza pelo ID.

Vamos fazer um for onde queremos o valor do índice e queremos vasculhar pizza por pizza. Então, for i, p vai ser igual a range, e vamos passar pizza por pizza. Vamos perguntar se a minha p.ID for igual ao ID que convertemos antes para um valor inteiro, isso significa que estamos na pizza que queremos deletar.

Vamos alterar a nossa lista de pizzas, de modo que pizzas = append(pizzas, ). Mas o append não é usado só para colocarmos valores na nossa lista? Não necessariamente, podemos usar o append no contexto que temos aqui, para alterar a forma como a nossa lista está.

Vamos pedir para pegar a lista de pizzas, exceto o ID que temos que recebemos. Vamos supor que pegamos o ID 2, então, queremos que ele pegue toda a minha lista de pizzas e pare no ID 2, não continue no ID 2.

Vamos colocar [:i], para ir até a pizza que queremos deletar. Depois precisamos de uma forma para pedir que ele continue pegando o resto das pizzas cadastradas.

Para isso, vamos usar uma propriedade um pouco diferente, vamos colocar uma vírgula e vamos escrever pizzas. Com isso, só de escrever pizzas, ele já inseriu para nós os três pontos, que é, pega todo o resto.

Entretanto, queremos que pegue o índice da pizza que queremos deletar, que, no caso, é a pizza 2, nesse exemplo que estamos dando, mais 1, i+ 1, pega o índice que temos mais 1.

Isso significa que ele vai pegar, a partir do ID 2, todas as outras pizzas, então, ID 3, 4 e assim por diante. Entretanto, precisamos dizer, continua, pega todo o resto, então, vamos usar dois pontos depois.

Depois que alteramos a nossa lista de pizzas, vamos escrever na linha a função savePizza() para gravar no nosso arquivo JSON e podemos exibir uma mensagem, c.JSON, passando o código 200, passamos uma mensagem com gin.H.

A mensagem pode ser, por exemplo, "message": "pizza deleted", algo desse tipo e damos um return, indicando que a nossa função acabou. Convertemos o ID para um valor inteiro e achamos a pizza que queremos deletar.

O último caso que deixamos para vocês, é aquele em que não achamos a pizza. Temos as pizzas com ID 1, 2 e 3 e o ID que colocamos foi o 42, a resposta é que não temos a pizza com ID 42. Nesse caso, podemos exibir uma mensagem com o código 404 e enviar uma mensagem, "message": "pizza not found, ou seja, pizza não encontrada.

    for i, p := range pizzas {
        if p.ID == id {
            pizzas = append(pizzas[:i], pizzas[i+1:]...)
            savePizza()
            c.JSON(200, gin.H{"message": "pizza deleted"})
            return
        }
    }
    c.JSON(404, gin.H{"message": "pizza not found"})
}

Vamos testar o código. Abrindo o nosso terminal, vamos executar o nosso comando go run, vamos no Thunderclient e vamos fazer um GET para todas as pizzas.

Ele está mostrando pizzas 1, 2, 3 e assim por diante. Queremos deletar a pizza 3, o atum com queijo Rust. Então, vamos fazer o seguinte, vamos colocar o método, ops, vamos colocar o método DELETE na pizza3, http://localhost:8080/pizzas/3. Vamos dar um delete e exibiu "pizza deleted", ou seja, se fazemos um GET agora para visualizar todas as pizzas, não vemos mais aquela pizza, e o mesmo aconteceu no nosso código, que contém a pizza de toscana e pizza de atum com queijo sendo exibido ali embaixo.

Nosso próximo desafio agora é editar, fazer uma atualização da pizza, então, queremos alterar o valor ou alguma outra informação. Vamos descobrir como que fazemos isso na sequência.

Sobre o curso Go: gerenciando e otimizando sua API

O curso Go: gerenciando e otimizando sua API possui 100 minutos de vídeos, em um total de 38 atividades. Gostou? Conheça nossos outros cursos de GoLang em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda GoLang acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas