Aula 09 – Golang para Web – Tratamento de erros

Aula 09 – Golang para Web – Tratamento de erros

Voltar para página principal do blog

Todas as aulas desse curso

Aula 08                       Aula 10

Tutorial Go para Web com Redis

Go para Web usando Redis

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.

Manipulação de erros

Vamos agora adicionar um tratamento básico de erros à nossa aplicação web.

Nas aulas anteriores, a gente tava retornando o manipulador quando um erro ocorria.

Escolhemos isso para evitar adicionar complexidade desnecessária, para poder cobrir um tópico de cada vez aqui no tutorial.

Retornar simplesmente um handler, na perspectiva do usuário será só uma página em branco, sem nenhuma explicação, o que não é muito amigável.

Em vez de fazer isso, vamos pelo menos retornar um código de status HTTP com uma mensagem de erro.

E também no login e no registro de usuário podemos colocar uma resposta mais significativa quando eles preenchem o formulário incorretamente.

Então bora lá!

Vamos a primeira situação em que ignoramos um erro, tá destacado em laranja no código logo abaixo.

web_app/main.go 


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("/", indexGetHandler).Methods("GET")
    r.HandleFunc("/", 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(":8000", nil)
}

//request hello handle
func indexGetHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    _, ok := session.Values["username"]
    if !ok {
	http.Redirect(w, r, "/login", 302)
	return
    }
    comments, err := client.LRange("comments", 0, 10).Result()
    if err != nil {
	return
    }
    templates.ExecuteTemplate(w, "index.html", comments)
}

func indexPostHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    comment := r.PostForm.Get("comment")
    client.LPush("comments", comment)
    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 != nil {
	return
    }
    err = bcrypt.CompareHashAndPassword(hash, []byte(password))
    if err != nil {
	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 {
	return
    }
    client.Set("user: "+username, hash, 0)
    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!")
}

Vamos tratar isso

Então, quando o browser receber o status code 500 por algum problema no redis por exemplo, ele vai imprimir a mensagem: Internal server error 

É a mensagem que aparecerá no browser.

Internal server error 

A alteração para tratar esse erro está destacado em azul.


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("/", indexGetHandler).Methods("GET")
    r.HandleFunc("/", 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(":8000", nil)
}

//request hello handle
func indexGetHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    _, ok := session.Values["username"]
    if !ok {
	http.Redirect(w, r, "/login", 302)
	return
    }
    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")
    client.LPush("comments", comment)
    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 != nil {
	return
    }
    err = bcrypt.CompareHashAndPassword(hash, []byte(password))
    if err != nil {
	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 {
    	return
    }
    client.Set("user: "+username, hash, 0)
    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!")
}

Agora na função indexPostHandler()

Na hora que o usuário for postar alguma coisa e houver problema no redis,  o usuário vai ver uma página em branco porque ainda não tratamos essa situação.

Vamos retornar pelo menos um código de status HTTP adequado.

Exatamente como fizemos na indexGetHandler().

Veja no código.

web_app/main.go


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("/", indexGetHandler).Methods("GET")
    r.HandleFunc("/", 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(":8000", nil)
}

//request hello handle
func indexGetHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    _, ok := session.Values["username"]
    if !ok {
        http.Redirect(w, r, "/login", 302)
        return
    }
    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.htmw.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal server error")) returnl", 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 != nil {
        return
    }
    err = bcrypt.CompareHashAndPassword(hash, []byte(password))
    if err != nil {
    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 {
            return
    }
    client.Set("user: "+username, hash, 0)
    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!")
}

Agora na função loginPostHandler()

Nesse caso podem ocorrer dois erros.

Uma situação seria: o redis está desligado, ocorrerá então um erro interno de servidor.

A outra situação é em relação ao nome de usuário e senha.

O usuário pode não estar registrado e querer logar, então o erro será do tipo usuário desconhecido.

Ou ele pode estar registrado, mas não digitar a senha corretamente.

Então são mais duas situações.

Veja no código as alterações para tratar esses casos.

web_app/main.go 


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("/", indexGetHandler).Methods("GET")
    r.HandleFunc("/", 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(":8000", nil)
}

//request hello handle
func indexGetHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    _, ok := session.Values["username"]
    if !ok {
	http.Redirect(w, r, "/login", 302)
        return
    }
    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.htmw.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal server error")) returnl", 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 {
	return
    }
    client.Set("user: "+username, hash, 0)
    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!")
}

Vamos fazer uma alteração também na web_app/templates/login.html

Adicionamos um class=”error” para poder depois estilizar, e dentro, o bind para passar o dado quando ocorrer um erro.

Enão, quando o erro chegar a esse template, ele irá exibir: unknow user onde tem o bind.

web_app/templates/login.html


<!DOCTYPE html>
<html>
  <head>
    <title>Login</title>
  </head>
  <body>
    {{ if . }}
    <div class="error">{{ . }}</div>
    {{ end }}
    <form method="POST">
      <div>Username: <input name="username"></div>
      <div>Password: <input name="password"></div>
      <div>
        <button type="submit">Login</button>
      </div>
    </form>
  </body>
</html>

Agora a registerPostHandler()

Vamos tratar a situação de erro de senha ou problema com bcrypt.

E também a situação em que o redis pode falhar.

Veja no código as alterações no código destacado em azul.

web_app/main.go 


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("/", indexGetHandler).Methods("GET")
    r.HandleFunc("/", 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(":8000", nil)
}

//request hello handle
func indexGetHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    _, ok := session.Values["username"]
    if !ok {
	http.Redirect(w, r, "/login", 302)
        return
    }
    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.htmw.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal server error")) returnl", 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 servidor com:


go run main.go

Acesse:

localhost:8000

Por agora é só, nos vemos próxima. 😉

Código da aula: Github

Voltar para página principal do blog

Todas as aulas desse curso

Aula 08                       Aula 10

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. 😉

 

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>