通过httprouter和redis框架搭建restful api服务

时间:2022-04-28
本文章向大家介绍通过httprouter和redis框架搭建restful api服务,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

生命不止,继续 go go go !!!

##**httprouter**

HttpRouter is a lightweight high performance HTTP request router (also called multiplexer or just mux for short) for Go.

github地址:

https://github.com/julienschmidt/httproute

获取:

go get github.com/julienschmidt/httproute

应用:

package main



import (

    "fmt"

    "log"

    "net/http"



    "github.com/julienschmidt/httprouter"

)



func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

    fmt.Fprint(w, "Welcome!n")

}



func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

    fmt.Fprintf(w, "hello, %s!n", ps.ByName("name"))

}



func main() {

    router := httprouter.New()

    router.GET("/", Index)

    router.GET("/hello/:name", Hello)



    log.Fatal(http.ListenAndServe(":8080", router))

}

##**redigo/redis**

golang中使用redis这里不再过多的介绍,之前的博客有写过:

Go实战--golang中使用redis(redigo和go-redis/redis)

package main



import (

    "fmt"



    "github.com/garyburd/redigo/redis"

)



func main() {

    c, err := redis.Dial("tcp", "127.0.0.1:6379")

    if err != nil {

        fmt.Println("Connect to redis error", err)

        return

    }

    defer c.Close()



    _, err = c.Do("SET", "mykey", "superWang")

    if err != nil {

        fmt.Println("redis set failed:", err)

    }



    username, err := redis.String(c.Do("GET", "mykey"))

    if err != nil {

        fmt.Println("redis get failed:", err)

    } else {

        fmt.Printf("Get mykey: %v n", username)

    }

}

##**搭建rest api**

参考地址:

https://medium.com/code-zen/rest-apis-server-in-go-and-redis-66e9cb80a71b

这里创建一个简单的博客系统,具有以下功能:

http://localhost/ 显示欢迎消息,

http://localhost/posts 显示所有posts

http://localhost/posts/id 根据id显示post

首先是写model:

**models.go**

如果你对于golang中结构体不是很了解,可以看看这篇博客:

Go语言学习之struct(The way to go)

*主要是字段标签*

package main



import (

    "time"

)



type User struct {

    Id          int        `json:"id"`

    Username string     `json:"username"`

    Email    string     `json:"email"`

}



type Comment struct {

    Id        int         `json:"id"`

    User      User         `json:"user"`

    Text      string     `json:"text"`

    Timestamp time.Time `json:"timestamp"`

}



type Post struct {

    Id        int        `json:"id"`

    User      User        `json:"user"`

    Topic     string    `json:"topic"`

    Text      string    `json:"text"`

    Comment   Comment   `json:"comment"`

    Timestamp time.Time `json:"timestamp"`

}



type Posts    []Post

type Comments []Comment

type Users    []Use

**routes.go**

列出所有的路由:

package main



import (

    mux "github.com/julienschmidt/httprouter"

)



type Route struct {

    Name    string

    Method  string

    Pattern string

    Handle  mux.Handle

}



type Routes []Route



var routes = Routes{

    Route{

        "Index",

        "GET",

        "/",

        Index,

    },

    Route{

        "PostIndex",

        "GET",

        "/posts",

        PostIndex,

    },

    Route{

        "PostShow",

        "GET",

        "/posts/:postId",

        PostShow,

    },

    Route{

        "PostCreate",

        "POST",

        "/posts",

        PostCreate,

    },

    Route{

        "PostDelete",

        "POST",

        "/posts/del/:postId",

        PostDelete,

    },

}

**handlers.go**

有路由就要有handler。

关于io/ioutil请参考下面博客:

《Go语言学习之ioutil包(The way to go)

关于log包请参考下面博客:

Go语言学习之log包(The way to go)

package main



import (

    "encoding/json"

    "fmt"

    "net/http"

    "io"

    "io/ioutil"

    "strconv"

    "log"

    "time"

    

    mux "github.com/julienschmidt/httprouter"

)



func Logger(r *http.Request) {

    

    start:= time.Now()

    

    log.Printf(

        "%st%st%qt%s",

        r.Method,

        r.RequestURI,

        r.Header,

        time.Since(start),

    )

}



func Index(w http.ResponseWriter, r *http.Request, _ mux.Params) {



    fmt.Fprintf(w, "<h1 style="font-family: Helvetica;">Hello, welcome to blog service</h1>")

}



func PostIndex(w http.ResponseWriter, r *http.Request, _ mux.Params) {



    w.Header().Set("Content-Type", "application/json; charset=UTF-8")

    w.WriteHeader(http.StatusOK)

    

    var posts Posts

    

    posts = FindAll()



    if err := json.NewEncoder(w).Encode(posts); err != nil {

        panic(err)

    }



}



func PostShow(w http.ResponseWriter, r *http.Request, ps mux.Params) {

    

    id, err := strconv.Atoi(ps.ByName("postId"))

    

    HandleError(err)



    post := FindPost(id)

    

    if err := json.NewEncoder(w).Encode(post); err != nil {

        panic(err)

    }

}



func PostCreate(w http.ResponseWriter, r *http.Request, _ mux.Params) {

    

    Logger(r)

    

    var post Post

    body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))

    HandleError(err)



    if err := r.Body.Close(); err != nil {

        panic(err)

    }



    // Save JSON to Post struct

    if err := json.Unmarshal(body, &post); err != nil {

        w.Header().Set("Content-Type", "application/json; charset=UTF-8")

        w.WriteHeader(422) // unprocessable entity

        if err := json.NewEncoder(w).Encode(err); err != nil {

            panic(err)

        }

    }



    CreatePost(post)

    w.Header().Set("Content-Type", "application/json; charset=UTF-8")

    w.WriteHeader(http.StatusCreated)

}



func PostDelete(w http.ResponseWriter, r *http.Request, ps mux.Params) {

    

    id, err := strconv.Atoi(ps.ByName("postId"))

    HandleError(err)



    DeletePost(id)

}

**router.go**

package main



import (

    mux "github.com/julienschmidt/httprouter"

)



func NewRouter() *mux.Router {



    router := mux.New()



    for _, route := range routes {    

        

        router.Handle(route.Method, route.Pattern, route.Handle)

    }



    return route

}

**db.go**

最后就是数据库相关操作了,这里使用redis。

这里需要注意的就是string到json的转换。

关于strconv的用法可以参考博客:

Go语言学习之strconv包(The way to go)

例如,将整数转换为十进制字符串形式

func Itoa(i int) string

关于json的用法可以参考博客:

Go语言学习之encoding/json包(The way to go)

关于time包的用法可以参考博客:

Go语言学习之time包(获取当前时间戳等)(the way to go)

例如:获取当前时间

func Now() Time

下面是db的完整代码:

package main



import (

    "fmt"

    "time"

    "encoding/json"

    "strconv"

    

    "github.com/garyburd/redigo/redis"

)



var currentPostId int

var currentUserId int



func RedisConnect() redis.Conn {

    c, err := redis.Dial("tcp", ":6379")

    HandleError(err)

    return c

}



// Give us some seed data

func init() {

    

    CreatePost(Post{

            User: User{

                    Username: "wangshubo",

                    Email: "wangshubo1989@126.com",

            },

            Topic: "My First Post",

            Text:  "Hello everyone! This is awesome.",

    })

    

    CreatePost(Post{

            User: User{

                    Username: "superWang",

                    Email: "wangshubo@gmail.com",

            },

            Topic: "Greeting",

            Text: "Greetings from Ironman",

    })

}



func FindAll() Posts {



    c := RedisConnect()

    defer c.Close()

    

    keys, err := c.Do("KEYS", "post:*")



    HandleError(err)

    

    var posts Posts

    

    for _, k := range keys.([]interface{}) {

        

        var post Post

        

        reply, err := c.Do("GET", k.([]byte))



        HandleError(err)

        

        if err := json.Unmarshal(reply.([]byte), &post); err != nil {

            panic(err)

        }

        posts = append(posts, post)

    }

    return posts

}



func FindPost(id int) Post {

    

    var post Post



    c := RedisConnect()

    defer c.Close()

    

    reply, err := c.Do("GET", "post:" + strconv.Itoa(id))



    HandleError(err)

    

    fmt.Println("GET OK")

    

    if err = json.Unmarshal(reply.([]byte), &post); err != nil {

        panic(err)

    }

    return post

}



func CreatePost(p Post) {

    

    currentPostId += 1

    currentUserId += 1

    

    p.Id = currentPostId

    p.User.Id = currentUserId

    p.Timestamp = time.Now()

    

    c := RedisConnect()

    defer c.Close()

    

    b, err := json.Marshal(p)



    HandleError(err)

    

    // Save JSON blob to Redis

    reply, err := c.Do("SET", "post:" + strconv.Itoa(p.Id), b)



    HandleError(err)

    

    fmt.Println("GET ", reply)

    

}



func DeletePost(id int) {

    

    c := RedisConnect()

    defer c.Close()

    

    reply, err := c.Do("DEL", "post:" + strconv.Itoa(id))

    HandleError(err)

    

    if reply.(int) != 1 {

        fmt.Println("No post removed")

    } else {

        fmt.Println("Post removed")

    }

}