Browse Source

New DB schema, JSON/XML/CSV serialization, Create/List users.

Frederic G. MARAND 9 years ago
parent
commit
fb5c893dcb
6 changed files with 425 additions and 0 deletions
  1. 34 0
      ch2/1json.go
  2. 35 0
      ch2/2xml.go
  3. 44 0
      ch2/3csv.go
  4. 178 0
      ch2/4api.go
  5. 67 0
      ch2/db0.sql
  6. 67 0
      ch2/db4.sql

+ 34 - 0
ch2/1json.go

@@ -0,0 +1,34 @@
+package main
+
+import (
+	"database/sql"
+	"encoding/json"
+	"fmt"
+	"net/http"
+
+	_ "github.com/go-sql-driver/mysql"
+)
+
+type User struct {
+	Name  string `json:"name"`
+	Email string `json:"email"`
+	ID    int    `json:"int"`
+}
+
+func userRouter(w http.ResponseWriter, r *http.Request) {
+	ourUser := User{
+		Name:  "Bill Smith",
+		Email: "bill.smith@example.com",
+		ID:    100,
+	}
+	output, _ := json.Marshal(&ourUser)
+	fmt.Fprint(w, string(output))
+}
+
+var database *sql.DB
+
+func main() {
+	fmt.Println("Starting JSON server")
+	http.HandleFunc("/user", userRouter)
+	http.ListenAndServe(":8080", nil)
+}

+ 35 - 0
ch2/2xml.go

@@ -0,0 +1,35 @@
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"net/http"
+
+	"encoding/xml"
+
+	_ "github.com/go-sql-driver/mysql"
+)
+
+type User struct {
+	Name  string `xml:"name"`
+	Email string `xml:"email"`
+	ID    int    `xml:"int"`
+}
+
+func userRouter(w http.ResponseWriter, r *http.Request) {
+	ourUser := User{
+		Name:  "Bill Smith",
+		Email: "bill.smith@example.com",
+		ID:    100,
+	}
+	output, _ := xml.MarshalIndent(&ourUser, "", "  ")
+	fmt.Fprint(w, string(output))
+}
+
+var database *sql.DB
+
+func main() {
+	fmt.Println("Starting XML server")
+	http.HandleFunc("/user", userRouter)
+	http.ListenAndServe(":8080", nil)
+}

+ 44 - 0
ch2/3csv.go

@@ -0,0 +1,44 @@
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"net/http"
+
+	"encoding/csv"
+
+	_ "github.com/go-sql-driver/mysql"
+	"strconv"
+)
+
+// Tags are not actually used at this point. Probably never.
+type User struct {
+	Name  string `csv:"name"`
+	Email string `csv:"email"`
+	ID    int    `csv:"int"`
+}
+
+func userRouter(w http.ResponseWriter, r *http.Request) {
+	ourUser := User{
+		Name:  "Bill Smith",
+		Email: "bill,smith@example.com",
+		ID:    100,
+	}
+
+	row := []string{
+		ourUser.Name,
+		ourUser.Email,
+		strconv.Itoa(ourUser.ID),
+	}
+	csvWriter := csv.NewWriter(w)
+	csvWriter.Write(row)
+	csvWriter.Flush()
+}
+
+var database *sql.DB
+
+func main() {
+	fmt.Println("Starting CSV server")
+	http.HandleFunc("/user", userRouter)
+	http.ListenAndServe(":8080", nil)
+}

+ 178 - 0
ch2/4api.go

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

+ 67 - 0
ch2/db0.sql

@@ -0,0 +1,67 @@
+-- MySQL dump 10.13  Distrib 5.5.43, for debian-linux-gnu (x86_64)
+--
+-- Host: localhost    Database: goweb_social_network
+-- ------------------------------------------------------
+-- Server version	5.5.43-0ubuntu0.12.04.1-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `users` (
+  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `user_nickname` varchar(32) NOT NULL,
+  `user_first` varchar(32) NOT NULL,
+  `user_last` varchar(32) NOT NULL,
+  `user_email` varchar(128) NOT NULL,
+  PRIMARY KEY (`user_id`),
+  UNIQUE KEY `user_nickname` (`user_nickname`),
+  UNIQUE KEY `user_email` (`user_email`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `users_relationships`
+--
+
+DROP TABLE IF EXISTS `users_relationships`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `users_relationships` (
+  `users_relationship_id` int(13) NOT NULL,
+  `from_user_id` int(10) NOT NULL,
+  `to_user_id` int(11) NOT NULL,
+  `users_relationship_type` varchar(10) NOT NULL,
+  `users_relationship_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`users_relationship_id`),
+  KEY `from_user_id` (`from_user_id`),
+  KEY `to_user_id` (`to_user_id`),
+  KEY `from_user_id_to_user_id` (`from_user_id`,`to_user_id`),
+  KEY `from_user_id_to_user_id_users_relationship_type` (`from_user_id`,`to_user_id`,`users_relationship_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2015-07-01 17:04:48

+ 67 - 0
ch2/db4.sql

@@ -0,0 +1,67 @@
+-- MySQL dump 10.13  Distrib 5.5.43, for debian-linux-gnu (x86_64)
+--
+-- Host: localhost    Database: goweb_social_network
+-- ------------------------------------------------------
+-- Server version	5.5.43-0ubuntu0.12.04.1-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `users`
+--
+
+DROP TABLE IF EXISTS `users`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `users` (
+  `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `user_name` varchar(32) NOT NULL,
+  `user_first` varchar(32) NOT NULL,
+  `user_last` varchar(32) NOT NULL,
+  `user_email` varchar(128) NOT NULL,
+  PRIMARY KEY (`user_id`),
+  UNIQUE KEY `user_nickname` (`user_name`),
+  UNIQUE KEY `user_email` (`user_email`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `users_relationships`
+--
+
+DROP TABLE IF EXISTS `users_relationships`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `users_relationships` (
+  `users_relationship_id` int(13) NOT NULL,
+  `from_user_id` int(10) NOT NULL,
+  `to_user_id` int(11) NOT NULL,
+  `users_relationship_type` varchar(10) NOT NULL,
+  `users_relationship_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`users_relationship_id`),
+  KEY `from_user_id` (`from_user_id`),
+  KEY `to_user_id` (`to_user_id`),
+  KEY `from_user_id_to_user_id` (`from_user_id`,`to_user_id`),
+  KEY `from_user_id_to_user_id_users_relationship_type` (`from_user_id`,`to_user_id`,`users_relationship_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+/*!40101 SET character_set_client = @saved_cs_client */;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2015-07-01  9:41:27