Go: RESTful - um exemplo completo com persistência
De Aulas
Afluentes: Sistemas Distribuídos e Mobile
Base de Dados
Tabela users
create table users (
id varchar(16) not null,
name varchar(50) not null,
email varchar(50) not null,
primary key (id)
);
database.go
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
// Database class
type Database struct {
connection *sql.DB
dbtype string
info string
err error
}
// NewDatabase Constructor
func NewDatabase(dbhost string, dbtype string, dbname string, dbuser string, dbpass string) *Database {
db := new(Database)
db.dbtype = dbtype
db.info = fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", dbuser, dbpass, dbname)
return db
}
// Get return connection
func (db *Database) Get() *sql.DB {
if db.connection == nil {
db.connection, db.err = sql.Open(db.dbtype, db.info)
}
return db.connection
}
// Close function
func (db *Database) Close() {
db.connection.Close()
}
user.go
package main
type User struct {
Id string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func NewUser(id string, name string, email string) *User {
user := new(User)
user.Id = id
user.Name = name
user.Email = email
return user
}
userdao.go
package main
import "fmt"
type UserDAO struct {
db *Database
users []*User
}
func NewUserDAO(db *Database) *UserDAO {
userdao := new(UserDAO)
userdao.db = db
userdao.users = make([]*User, 0)
return userdao
}
func (userdao *UserDAO) GetAll() []*User {
userdao.users = make([]*User, 0)
db := userdao.db.Get()
rows, err := db.Query("select * from users")
if err != nil {
fmt.Println(err.Error())
}
for rows.Next() {
var id string
var name string
var email string
rows.Scan(&id, &name, &email)
userdao.users = append(userdao.users, NewUser(id, name, email))
}
return userdao.users
}
func (userdao *UserDAO) Insert(user *User) {
db := userdao.db.Get()
query := "insert into users (id, name, email) values ($1, $2, $3)"
_, err := db.Query(query, user.Id, user.Name, user.Email)
if err != nil {
fmt.Println(err.Error())
}
}
func (userdao *UserDAO) Get(id string) *User {
db := userdao.db.Get()
query := "select * from users where id=$1"
rows, err := db.Query(query, id)
if err != nil {
fmt.Println(err.Error())
}
if rows.Next() {
var id string
var name string
var email string
rows.Scan(&id, &name, &email)
return NewUser(id, name, email)
}
return nil
}
func (userdao *UserDAO) Update(user *User) string {
db := userdao.db.Get()
query := "update users set name=$2, email=$3 where id=$1"
_, err := db.Query(query, user.Id, user.Name, user.Email)
if err != nil {
fmt.Println(err.Error())
return err.Error()
}
return "ok"
}
func (userdao *UserDAO) Delete(id string) string {
db := userdao.db.Get()
query := "delete from users where id=$1"
_, err := db.Query(query, id)
if err != nil {
fmt.Println(err.Error())
return err.Error()
}
return "ok"
}
main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
)
func main() {
db := NewDatabase("localhost", "postgres", "distribuidos", "saulo", "1234")
defer db.Close()
dao := NewUserDAO(db)
router := http.NewServeMux()
router.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
var user User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
dao.Insert(NewUser(user.Id, user.Name, user.Email))
})
router.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
users := dao.GetAll()
json.NewEncoder(w).Encode(users)
})
router.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
id := r.PathValue("id")
user := dao.Get(id)
json.NewEncoder(w).Encode(user)
})
router.HandleFunc("PUT /users", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
var user User
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
out := dao.Update(NewUser(user.Id, user.Name, user.Email))
json.NewEncoder(w).Encode(out)
})
router.HandleFunc("DELETE /users/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
id := r.PathValue("id")
out := dao.Delete(id)
json.NewEncoder(w).Encode(out)
})
// Vamos precisar do OPTIONS porque o PUT primeiro manda um OPTION
// Assim, se for localhost, avisamos o CORS que permitimos local
router.HandleFunc("OPTIONS /users", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
})
// O DELETE também chama o OPTIONS, mas envia também uma informação
router.HandleFunc("OPTIONS /users/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
})
err := http.ListenAndServe("localhost:8080", router)
if err != nil {
fmt.Println(err.Error())
}
}