Our First API in Go

Gorilla

$ go get github.com/gorilla/mux import "github.com/gorilla/mux"

package main

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "net/http"
)

type API struct {
    Message string `json:"message"`
}

func hello(w http.ResponseWriter, r *http.Request) {
    urlParams := mux.Vars(r)
    name := urlParams["user"]
    HelloMessage := "hello, " + name
    message := API{HelloMessage}
    output, err := json.Marshal(message)

    if err != nil {
        fmt.Println("Something went wrong")

    }
    fmt.Fprintf(w, string(output))

}
func main() {
    r := mux.NewRouter()
    r.HandleFunc("/api/{user:[0-9]+}", hello)
    http.Handle("/", r)
    http.ListenAndServe(":8080", nil)
}

Routes

在路由方面的三方包有Pat, Routes, Gorilla Mux工具箱. 三者中Pat的性能最好,但路由比较复杂的性况下还是使用Routes或者 Gorilla. 建议使用Gorilla Mux

Setting data via HTTP

现在我们已经测试了如何处理路由,现在让我们通过REST终端来向数据库注入数据。

在这种情况下,我们会使用POST方法来请求,因为它可以传输大量的数据。可以避免GET请求对数据长度的限制。

从技术上讲,在create-read-update-delete(CRUD)中,PUT是用来创建数据的。但多年来都不使用PUT, 而是作为教程的注解来讲。最近,有人支持恢复PUT(and DELETE)的相应地位。Go(and Gorilla)允许你将请求归入到PUT(或者DELETE)

Connecting to MySQL

Go有大量的内置数据库连接设施和第三方数据库连接包。Go默认的SQL包是database/sql, 它允许利用一些通用的标准连接所有的数据库。

然而,我们并不需要创建自己的Mysql连接(至少现在不需要),我们可以使用第三方的库。这里有许多这样的库,但我们将使用Go-Mysql-Driver.

你可以通过 go get github.com/go-sql-driver/mysql 安装

对于本书中的例子,我们假设MySQL运行在本地的3306标准端口上。如果不是,你必须调整例子。

我们在导入包的地方,有两点增加:sql包和上面的MySQL驱动。但MySQl驱动前有下划线

package main

import
(
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "net/http"
)

我们将依然使用Gorilla来增加一个端点,用来设置或者创建数据,我们将使用 PUT或者POST, 但对于这个例子,在URL中添加参数是最简单的方式。以下是设置的路径:

routes := mux.NewRouter()
routes.HandleFunc("/api/user/create", CreateUser).Methods("GET")

我们的CreateUser函数将接收多个参数——user, email, first, and last. 以下是整个User struct结构:

type User struct {
    ID int "json:id"
    Name string "json:username"
    Email string "json:email"
    First string "json:first"
    Last string "json:last"
}

以下让我们看看CreateUser函数:

func CreateUser(w http.ResponseWriter, r *http.Request) {
    NewUser := User{}
    NewUser.Name = r.FormValue("user")
    NewUser.Email = r.FormValue("email")
    NewUser.First = r.FormValue("first")
    NewUser.Last = r.FormValue("last")
    output, err := json.Marshal(NewUser)
    fmt.Println(string(output))
    if err != nil {
        fmt.Println("Something went wrong!")
    }
    sql := "INSERT INTO users 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)
}

数据库表的结构如下

create database social_network;
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 INDEX user_nickname (user_nickname)
)

传递的地址为

http://localhost:8080/api/user/create? user=nkozyra&first=Nathan&last=Kozyra&[email protected].

以下是整个程序

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"username"`
    Email string `json:"email"`
    First string `json:"first"`
    Last  string `json:"last"`
}

var db *sql.DB

func CreateUser(w http.ResponseWriter, r *http.Request) {
    NewUser := User{}
    NewUser.Name = r.FormValue("user")
    NewUser.Email = r.FormValue("email")
    NewUser.First = r.FormValue("first")
    NewUser.Last = r.FormValue("last")
    output, err := json.Marshal(NewUser)
    fmt.Println(string(output))
    if err != nil {
        fmt.Println("Something went wrong!")
    }

    stmt, err := db.Prepare("INSERT INTO users(user_nickname, user_first, user_last, user_email) values(?, ?, ?, ?)")

    if err != nil {
        fmt.Println(err)
    }
    defer stmt.Close()
    result, err := stmt.Exec(NewUser.Name, NewUser.First, NewUser.Last, NewUser.Email)
    if err != nil {
        fmt.Println(err)
    }

    affect, err := result.RowsAffected()
    if err != nil {
        panic(err)
    }
    fmt.Printf("%d\n", affect)

}
func GetUser(w http.ResponseWriter, r *http.Request) {

    w.Header().Set("Pragma", "no-cache")

    urlParams := mux.Vars(r)
    id := urlParams["id"]
    ReadUser := User{}
    err := db.QueryRow("select * from users where user_id=?", id).Scan(&ReadUser.ID, &ReadUser.Name, &ReadUser.First, &ReadUser.Last, &ReadUser.Email)
    switch {
    case err == sql.ErrNoRows:
        fmt.Fprintf(w, "No such user")
    case err != nil:
        log.Fatal(err)
    default:
        output, _ := json.Marshal(ReadUser)
        fmt.Fprintf(w, string(output))
    }
}
func main() {
    r := mux.NewRouter()
    r.HandleFunc("/api/user/create", CreateUser).Methods("GET")
    r.HandleFunc("/api/user/read/{id:[0-9]+}", GetUser)
    http.Handle("/", r)

    d, err := sql.Open("mysql", "root:root@/social_network")
    if err != nil {
        fmt.Println("aa")
        panic(err.Error())
    }
    defer d.Close()

    err = d.Ping()
    if err != nil {
        panic(err.Error())
    }
    db = d
    http.ListenAndServe(":8080", nil)
}