Browse Source

Add Link header for paging.

Frederic G. MARAND 9 years ago
parent
commit
55f968a567
2 changed files with 193 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 191 0
      ch2/5api-link.go

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+.idea
+*.iml

+ 191 - 0
ch2/5api-link.go

@@ -0,0 +1,191 @@
+/*
+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)
+}