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