Aula 10 - Golang para Web - Middleware

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no Facebook

Link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Código da aula: Github

Melhore seu NETWORKING

Participe de comunidades de desenvolvedores:

Fiquem a vontade para me adicionar ao linkedin.

E também para me seguir no GITHUB.

Ah, se puder, clica na estrela nos meus repositórios pra dá uma força ao meu perfil no GITHUB.

Middleware

O termo Middleware em geral é usado para agrupar tecnologias que estão entre a aplicação final e as fontes de dados. No nosso caso, o que iremos faze é anexar uma funcionalidade aos nossos manipuladores existentes. Vamos usar um middleware para a autenticação. Se olharmos para o nosso código, o indexGetHandler(), ele é o único manipulador que temos até agora, que requer autenticação. Se tivermos outros locais no código, outros endpoints, outros handlers que precisem usar autenticação, a gente teria que copiar e colar o código que lida com a autenticação, como o que já temos no indexGetHandler(), nesses vários locais. Mas, essa repetição de código não seria bom, não é uma boa prática, então, é melhor isolar esse código em um middleware e chamar ele onde for preciso usar autenticação. 😉

A ideia é uma abordagem funcional

Precisamos criar um função que recebe como entrada um de nossos manipuladores (handlers) e retorna um novo manipulador. Então, a primeira coisa que vamos fazer é implementar esta função middleware, a AuthRequired(), no web_app/main.go. Ela vai retornar uma função com a mesma assinatura de indexGetHandler() que chama o método HTTP do handler que recebeu como entrada, no caso do nosso exemplo, o método HTTP do indexGetHandler(). O resultado é basicamente a execução do indexGetHandler(). É dessa forma que os middlewares funcionam, eles interceptam um outro handler, nesse caso de exemplo, handlers que precisem de autenticação.

Na AuthRequired() o que acontece é:

É verificado se tá tudo ok com as credenciais de acesso, se não tiver, o usuário é redirecionado para a página de login e o indexGetHandler() não é chamado. Nas rotas, vamos colocar a chamada da AuthRequired() não só para o indexGetHandler() como também para o indexPostHandler(), porque não queremos que uma pessoa não logada possa ver ou postar comentários, só os usuários logados poderão ver e postar. Com esse middleware, evitamos duplicação de código.

package main

import (
	"html/template"
	"net/http"

	"github.com/go-redis/redis"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"golang.org/x/crypto/bcrypt"
)

//globals variables
var client *redis.Client
var store = sessions.NewCookieStore([]byte("t0p-s3cr3t"))
var templates *template.Template

func main() {
	client = redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	templates = template.Must(template.ParseGlob("templates/*.html"))
	r := mux.NewRouter()
        r.HandleFunc("/", AuthRequired(indexGetHandler)).Methods("GET")
        r.HandleFunc("/", AuthRequired(indexPostHandler)).Methods("POST")
	r.HandleFunc("/login", loginGetHandler).Methods("GET")
	r.HandleFunc("/login", loginPostHandler).Methods("POST")
	r.HandleFunc("/register", registerGetHandler).Methods("GET")
	r.HandleFunc("/register", registerPostHandler).Methods("POST")
	fs := http.FileServer(http.Dir("./static/"))
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
	http.Handle("/", r)
	http.ListenAndServe(":8001", nil)
}

func AuthRequired(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request){
 	session, _ := store.Get(r, "session")
 	_, ok := session.Values["username"]
 	if !ok {
 		http.Redirect(w, r, "/login", 302)
 		return
 	}
	handler.ServeHTTP(w, r)
	}
}

//request hello handle
func indexGetHandler(w http.ResponseWriter, r *http.Request) {
 	comments, err := client.LRange("comments", 0, 10).Result()
 	if err != nil{
 		w.WriteHeader(http.StatusInternalServerError)
 		w.Write([]byte("Internal server error"))
 		return
 	}
 	templates.ExecuteTemplate(w, "index.html", comments)
 }

func indexPostHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	comment := r.PostForm.Get("comment")
	err := client.LPush("comments", comment).Err()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Internal server error"))
		return
	}
	http.Redirect(w, r, "/", 302)
}

func loginGetHandler(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "login.html", nil)
}

func loginPostHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	username := r.PostForm.Get("username")
	password := r.PostForm.Get("password")
	hash, err := client.Get("user: " + username).Bytes()
	if err == redis.Nil {
		templates.ExecuteTemplate(w, "login.html", "unknown user")
		return
	} else if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Internal server error"))
		return
	}
	err = bcrypt.CompareHashAndPassword(hash, []byte(password))
	if err != nil {
		templates.ExecuteTemplate(w, "login.html", "invalid login")
		return
	}
	session, _ := store.Get(r, "session")
	session.Values["username"] = username
	session.Save(r, w)
	http.Redirect(w, r, "/", 302)
}

func registerGetHandler(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "register.html", nil)
}

func registerPostHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	username := r.PostForm.Get("username")
	password := r.PostForm.Get("password")
	cost := bcrypt.DefaultCost
	hash, err := bcrypt.GenerateFromPassword([]byte(password), cost)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Internal server error"))
		return
	}
	err = client.Set("user: "+username, hash, 0).Err()
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Internal server error"))
		return
	}
	http.Redirect(w, r, "/login", 302)
}

//request contact page handle
func contactHandler(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "contact.html", "This is the contact page!")
}

//request about page handle
func aboutHandler(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "about.html", "This is the about page!")
}

Ligue o redis-server com:


redis-server
 

Ligue o servidor com:


go run main.go

Acesse:

localhost:8000

Por agora é só, nos vemos próxima. ;)

Código da aula: Github

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no Facebook

Link do código fluente no Pinterest

Novamente deixo meus link de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado, até a próxima e bons estudos. ;)