/* Available routes: /api/users OPTIONS Expose available actions /api/users GET Return user list, possibly filtered /api/users POST Create user /api/users/123 GET Return user 123 -> not listed in book /api/users/123 PUT Update user 123 /api/users/123 DELETE Delete user 123 */ package main import ( "database/sql" "encoding/json" "fmt" "log" "net/http" "net/url" "strconv" _ "github.com/go-sql-driver/mysql" "github.com/gorilla/mux" ) var database *sql.DB type Users struct { Users []User `json:"users"` } type User struct { ID int `json:"id"` Name string `json:"username"` Email string `json:"mail"` First string `json:"first"` Last string `json:"last"` } /* UserCreate creates a new user and stores it. The SQL query is vulnerable to injection, so the route matching needs be safe. */ func UserCreate(w http.ResponseWriter, r *http.Request) { NewUser := User{ Name: r.FormValue("name"), Email: r.FormValue("mail"), First: r.FormValue("first"), Last: r.FormValue("last"), } output, err := json.MarshalIndent(NewUser, "", " ") fmt.Println(string(output)) if err != nil { fmt.Println("Something went wrong!") } sql := "INSERT INTO user SET user_nickname='" + NewUser.Name + "', user_first='" + NewUser.First + "', user_last='" + NewUser.Last + "', user_email='" + NewUser.Email + "'" q, err := database.Exec(sql) if err != nil { fmt.Println(err) } fmt.Println(q) } func UsersRetrieve(w http.ResponseWriter, r *http.Request) { w.Header().Set("Pragma", "no-cache") start := 0 limit := 10 next := start + limit requestUrl := r.URL requestUrl.RawQuery = url.QueryEscape("start=" + strconv.Itoa(next) + "&foo=ba r") w.Header().Set("Link", "<"+requestUrl.String()+">; rel=\"next\"") rows, err := database.Query(` SELECT u.user_id, u.user_name, u.user_email, u.user_first, u.user_last FROM users u LIMIT 10 `) if err != nil { fmt.Fprintln(w, "Something went wrong!") log.Fatal(err) } Response := Users{} for rows.Next() { user := User{} rows.Scan(&user.ID, &user.Name, &user.Email, &user.First, &user.Last) Response.Users = append(Response.Users, user) } output, _ := json.MarshalIndent(Response, "", " ") fmt.Fprintln(w, string(output)) } type API struct { Message string `json:"message"` } func UserMethods(w http.ResponseWriter, r *http.Request) { } func UserReplace(w http.ResponseWriter, r *http.Request) { } func UserDelete(w http.ResponseWriter, r *http.Request) { } func UserRetrieve(w http.ResponseWriter, r *http.Request) { w.Header().Set("Pragma", "no-cache") vars := mux.Vars(r) id := vars["id"] user := User{} var result API sqlQuery := ` SELECT u.user_id, u.user_nickname, u.user_first, u.user_last, u.user_email FROM user u WHERE u.user_id = ? ` stmt, err := database.Prepare(sqlQuery) if err != nil { log.Fatal(err.Error()) } row := stmt.QueryRow(id) scanErr := row.Scan(&user.ID, &user.Name, &user.First, &user.Last, &user.Email) switch { case scanErr == sql.ErrNoRows: // FIXME XSS result = API{Message: fmt.Sprintf("No such user: %s", id)} json, err := json.MarshalIndent(result, "", " ") if err != nil { log.Fatal(err.Error()) } w.Write(json) case err != nil: // FIXME XSS result = API{Message: fmt.Sprintf("Error reading user: %s", id)} json, errIndent := json.MarshalIndent(result, "", " ") if errIndent != nil { log.Fatal(errIndent.Error()) } w.Write(json) log.Fatal(err.Error()) case err == nil: json, errIndent := json.MarshalIndent(user, "", " ") if errIndent != nil { log.Fatal(errIndent.Error()) } w.Write(json) } } func main() { var err error db, err := sql.Open("mysql", "goroot:gopass@/goweb_social_network") if err != nil { log.Fatal(err.Error()) } database = db routes := mux.NewRouter() routes.HandleFunc("/api/users", UserCreate).Methods("POST") routes.HandleFunc(`/api/users`, UsersRetrieve).Methods("GET") // Not yet implemented. routes.HandleFunc(`/api/users`, UserMethods).Methods("OPTIONS") routes.HandleFunc(`/api/users/{id:[\d]+}`, UserRetrieve).Methods("GET") routes.HandleFunc(`/api/users/{id:[\d]+}`, UserReplace).Methods("PUT") routes.HandleFunc(`/api/users/{id:[\d]+}`, UserDelete).Methods("DELETE") // -------------------- http.Handle("/", routes) http.ListenAndServe(":8080", nil) }