Dando continuidade ao meu artigo sobre CRUD completo com Go e MongoDB, hoje mostrarei como adicionar o Redis em nossa estrutura.
Para pular a etapa de criação de um novo projeto, eu irei utilizar o mesmo desenvolvido do artigo anterior. Caso tenha interesse em clonar ele, segue o link no meu GitHub:
O projeto está bem simples. Nós criamos as principais rotas de um CRUD e configuramos o projeto para armazenar os nossos dados em um banco de dados MongoDB. Nosso primeiro passo será configurar o redis.
Configurando o Redis
Para não precisar instalar o Redis no meu computador, eu irei utilizar ele da mesma forma que estou utilizando o mongoDB: em um container Docker. Caso tenha interesse em saber como criar um contêiner com Redis, segue o link de um artigo onde eu demonstro esse passo em um ambiente Windows:
Agora, caso esteja em um ambiente Linux, basta executar o comando abaixo no seu terminal. Esse comando criará um container com Redis utilizando a porta default 6379.
docker run --name some-redis -p 6379:6379 -d redis
Para validar o acesso eu irei utilizar o Redis Desktop Manager. Caso não conheça ele, o Redis Desktop Manager é um utilitário gratuito que nos permite gerenciar o Redis através de uma interface gráfica.
Abaixo você tem uma imagem demonstrando como configurar ele e o resultado do nosso teste de conexão:
Para os próximos passos será necessário uma IDE. Eu irei utilizar o Visual Studio Code, mas você pode utilizar um de sua preferência.
Com o projeto aberto no editor de textos, importaremos os pacotes necessários para os próximos passos. Para isso, abra um terminal no seu computador e execute o comando abaixo:
go get github.com/garyburd/redigo/redis
O próximo passo será criar o arquivo de configuração com o Redis. Para isso, adicione um novo arquivo chamado conn.go dentro do diretório config/redis. Abaixo você tem uma imagem demonstrando esse passo:
Agora atualize ele com o trecho de código abaixo:
package redis
import (
"fmt"
. "go-mongo-redis/config"
"github.com/garyburd/redigo/redis"
)
const (
redisExpire = 60
)
func RedisConnect() redis.Conn {
c, err := redis.Dial("tcp", ":6379")
HandleError(err)
return c
}
func Set(key string, value []byte) error {
conn := RedisConnect()
defer conn.Close()
_, err := conn.Do("SET", key, []byte(value))
HandleError(err)
conn.Do("EXPIRE", key, redisExpire) //10 Minutes
return err
}
func Get(key string) ([]byte, error) {
conn := RedisConnect()
defer conn.Close()
var data []byte
data, err := redis.Bytes(conn.Do("GET", key))
if err != nil {
return data, fmt.Errorf("error getting key %s: %v", key, err)
}
return data, err
}
Analisando o código acima, nós temos:
- Linha 15 a 19: conexão com o redis na porta default “6379”
- Linha 21 a 32: estamos recebendo o valor de uma chave para armazenar os dados que irão vir em bytes, em seguida passamos para o redis expirar esses valores depois de um minuto
- Linha 34 a 44: estamos recebendo o valor de uma chave e buscando no redis os valores armazenados nela
Agora, para testarmos o nosso código, abra o arquivo movie_router.go
e atualize o método GetAll
com o trecho de código abaixo:
func GetAll(w http.ResponseWriter, r *http.Request) {
movies := []Movie{}
reply, err := redis.Get("movies")
if err != nil {
fmt.Println("Buscando no mongoDB")
movies, err := dao.GetAll()
HandleError(err)
m, err := json.Marshal(movies)
HandleError(err)
redis.Set("movies", []byte(m))
respondWithJson(w, http.StatusOK, movies)
} else {
fmt.Println("Buscando no redis")
json.Unmarshal(reply, &movies)
respondWithJson(w, http.StatusOK, movies)
}
}
Em seguida, adicione o método no final do arquivo “movie_router.go”:
func handleError(err error) {
if err != nil {
panic(err)
}
}
Testando o código
Para testar o código abra um terminal e execute o comando go run main.go
. Esse comando executará nosso código na porta 3000.
Para cadastrarmos um novo registro e validar o cache, irei utilizar o Postman. Para quem não conhece, o Postman é uma ferramenta gratuita que nos auxilia nos testes a requisições HTTP.
Com ele aberto, preencha os campos conforme está na imagem abaixo e clique em Send:
Corpo do post:
{
"name": "The Equalizer",
"description": "Robert McCall (Denzel Washington), a man of mysterious origin who believes he has put the past behind him, dedicates himself to creating a quiet new life. However, when he meets Teri (Chloë Grace Moretz), a teenager who has been manhandled by violent Russian mobsters, he simply cannot walk away. With his set of formidable skills, McCall comes out of self-imposed retirement and emerges as an avenging angel, ready to take down anyone who brutalizes the helpless.",
"thumb_image": "http://t2.gstatic.com/images?q=tbn:ANd9GcQkGfxoavBEp4fR6P-yi2mIkUl1aZHHFIietLK4GriI5YyvGSJ7",
"active": true
}
Agora abra o endereço abaixo no seu navegador.
http://localhost:3000/api/v1/movies
Em seguida, atualize essa o seu navegador com essa rota “f5”. Caso tudo esteja funcionando corretamente, você irá receber uma mensagem na sua console informando quando os dados vierem do banco Mongo ou do Redis:
Organizando o código
Com o nosso código funcionando, vamos refatorar o nosso pacote movie_router.go.
Crie um novo diretório chamado helper dentro da sua aplicação. Em seguida, adicione um novo arquivo chamado comum.go dentro dele e atualize-o com o trecho de código abaixo:
package comum
import (
"encoding/json"
"net/http"
)
func RespondWithError(w http.ResponseWriter, code int, msg string) {
RespondWithJson(w, code, map[string]string{"error": msg})
}
func RespondWithJson(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
func HandleError(err error) {
if err != nil {
panic(err)
}
}
Em seguida, atualize o arquivo movie_router.go
, com o trecho de código abaixo:
package movierouter
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
. "github.com/programadriano/go-restapi/config/dao"
helper "github.com/programadriano/go-restapi/config/helper"
redis "github.com/programadriano/go-restapi/config/redis"
. "github.com/programadriano/go-restapi/models"
"gopkg.in/mgo.v2/bson"
)
var dao = MoviesDAO{}
func GetAll(w http.ResponseWriter, r *http.Request) {
movies := []Movie{}
reply, err := redis.Get("movies")
if err != nil {
fmt.Println("Buscando no mongoDB")
movies, err := dao.GetAll()
helper.HandleError(err)
m, err := json.Marshal(movies)
helper.HandleError(err)
redis.Set("movies", []byte(m))
helper.RespondWithJson(w, http.StatusOK, movies)
} else {
fmt.Println("Buscando no redis")
json.Unmarshal(reply, &movies)
helper.RespondWithJson(w, http.StatusOK, movies)
}
}
func GetByID(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
movie, err := dao.GetByID(params["id"])
if err != nil {
helper.RespondWithError(w, http.StatusBadRequest, "Invalid Movie ID")
return
}
helper.RespondWithJson(w, http.StatusOK, movie)
}
func Create(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var movie Movie
if err := json.NewDecoder(r.Body).Decode(&movie); err != nil {
helper.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
movie.ID = bson.NewObjectId()
if err := dao.Create(movie); err != nil {
helper.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
helper.RespondWithJson(w, http.StatusCreated, movie)
}
func Update(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := mux.Vars(r)
var movie Movie
if err := json.NewDecoder(r.Body).Decode(&movie); err != nil {
helper.RespondWithError(w, http.StatusBadRequest, "Invalid request payload")
return
}
if err := dao.Update(params["id"], movie); err != nil {
helper.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
helper.RespondWithJson(w, http.StatusOK, map[string]string{"result": movie.Name + " atualizado com sucesso!"})
}
func Delete(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := mux.Vars(r)
if err := dao.Delete(params["id"]); err != nil {
helper.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
helper.RespondWithJson(w, http.StatusOK, map[string]string{"result": "success"})
}
Em resumo nós retiramos de dentro do pacote movie_router.go os métodos ResponseWithError,RespondeWithJson e HandlerError e passamos para um outro pacote que pode ser utilizado nas nossas outras rotas.
Caso tenha interesse em baixar a versão final do código desenvolvido neste artigo, segue o seu link no meu GitHub:
Com isso finalizo mais esse artigo. Espero que tenham gostado e que ele possa ajudar!