Go实战--golang中使用echo框架、MongoDB、JWT搭建REST API(labstack/echo、gopkg.in/mgo.v2、dgrijalva/jwt-go)

来源:互联网 发布:python微信二次开发 编辑:程序博客网 时间:2024/04/28 13:24

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

之前介绍过golang中restful api的博客,是使用redis作为持久化,httprouter作为框架:
Go实战–通过httprouter和redis框架搭建restful api服务(github.com/julienschmidt/httprouter)

今天,继续echo框架,这次加入mongodb作为持久化存储,使用jwt进行验证,来搭建一套rest api,类似Twitter。

其中,很多知识点之前都有介绍过:
关于golang中使用mongodb科技参考:
Go实战–golang使用ssl连接MongoDB(mgo)

Go实战–golang中使用MongoDB(mgo)

关于golang中的使用jwt(JSON Web Token):
Go实战–golang中使用JWT(JSON Web Token)

代码结构:

./model   post.go   user.go./handler   handler.go   post.go   user.gomain.go

model

这里没有什么好说的,就是建立model,需要注意的就是golang中struct中的标签。
一个用户user,一个邮箱post。

user.go

package modelimport (    "gopkg.in/mgo.v2/bson")type (    User struct {        ID        bson.ObjectId `json:"id" bson:"_id,omitempty"`        Email     string        `json:"email" bson:"email"`        Password  string        `json:"password,omitempty" bson:"password"`        Token     string        `json:"token,omitempty" bson:"-"`        Followers []string      `json:"followers,omitempty" bson:"followers,omitempty"`    })

post.go

package modelimport (    "gopkg.in/mgo.v2/bson")type (    Post struct {        ID      bson.ObjectId `json:"id" bson:"_id,omitempty"`        To      string        `json:"to" bson:"to"`        From    string        `json:"from" bson:"from"`        Message string        `json:"message" bson:"message"`    })

handler

handler.go
handler中提出出来的公共部分。

package handlerimport (    "gopkg.in/mgo.v2")type (    Handler struct {        DB *mgo.Session    })const (    // Key (Should come from somewhere else).    Key = "secret")

post.go
post中加入两个功能,一个创建post,一个拉取post。
关于”net/http”可以参考:
Go语言学习之net/http包(The way to go)
关于”strconv”可以参考:
Go语言学习之strconv包(The way to go)

package handlerimport (    "go_echo_examples/twitter/model"    "net/http"    "strconv"    "github.com/labstack/echo"    "gopkg.in/mgo.v2"    "gopkg.in/mgo.v2/bson")func (h *Handler) CreatePost(c echo.Context) (err error) {    u := &model.User{        ID: bson.ObjectIdHex(userIDFromToken(c)),    }    p := &model.Post{        ID:   bson.NewObjectId(),        From: u.ID.Hex(),    }    if err = c.Bind(p); err != nil {        return    }    // Validation    if p.To == "" || p.Message == "" {        return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid to or message fields"}    }    // Find user from database    db := h.DB.Clone()    defer db.Close()    if err = db.DB("twitter").C("users").FindId(u.ID).One(u); err != nil {        if err == mgo.ErrNotFound {            return echo.ErrNotFound        }        return    }    // Save post in database    if err = db.DB("twitter").C("posts").Insert(p); err != nil {        return    }    return c.JSON(http.StatusCreated, p)}func (h *Handler) FetchPost(c echo.Context) (err error) {    userID := userIDFromToken(c)    page, _ := strconv.Atoi(c.QueryParam("page"))    limit, _ := strconv.Atoi(c.QueryParam("limit"))    // Defaults    if page == 0 {        page = 1    }    if limit == 0 {        limit = 100    }    // Retrieve posts from database    posts := []*model.Post{}    db := h.DB.Clone()    if err = db.DB("twitter").C("posts").        Find(bson.M{"to": userID}).        Skip((page - 1) * limit).        Limit(limit).        All(&posts); err != nil {        return    }    defer db.Close()    return c.JSON(http.StatusOK, posts)}

user.go
这部分包括用户注册、登录、添加follow。
关于time包可以参考:
Go语言学习之time包(获取当前时间戳等)(the way to go)

package handlerimport (    "go_echo_examples/twitter/model"    "net/http"    "time"    "github.com/dgrijalva/jwt-go"    "github.com/labstack/echo"    "gopkg.in/mgo.v2"    "gopkg.in/mgo.v2/bson")func (h *Handler) Signup(c echo.Context) (err error) {    // Bind    u := &model.User{ID: bson.NewObjectId()}    if err = c.Bind(u); err != nil {        return    }    // Validate    if u.Email == "" || u.Password == "" {        return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid email or password"}    }    // Save user    db := h.DB.Clone()    defer db.Close()    if err = db.DB("twitter").C("users").Insert(u); err != nil {        return    }    return c.JSON(http.StatusCreated, u)}func (h *Handler) Login(c echo.Context) (err error) {    // Bind    u := new(model.User)    if err = c.Bind(u); err != nil {        return    }    // Find user    db := h.DB.Clone()    defer db.Close()    if err = db.DB("twitter").C("users").        Find(bson.M{"email": u.Email, "password": u.Password}).One(u); err != nil {        if err == mgo.ErrNotFound {            return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"}        }        return    }    //-----    // JWT    //-----    // Create token    token := jwt.New(jwt.SigningMethodHS256)    // Set claims    claims := token.Claims.(jwt.MapClaims)    claims["id"] = u.ID    claims["exp"] = time.Now().Add(time.Hour * 72).Unix()    // Generate encoded token and send it as response    u.Token, err = token.SignedString([]byte(Key))    if err != nil {        return err    }    u.Password = "" // Don't send password    return c.JSON(http.StatusOK, u)}func (h *Handler) Follow(c echo.Context) (err error) {    userID := userIDFromToken(c)    id := c.Param("id")    // Add a follower to user    db := h.DB.Clone()    defer db.Close()    if err = db.DB("twitter").C("users").        UpdateId(bson.ObjectIdHex(id), bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil {        if err == mgo.ErrNotFound {            return echo.ErrNotFound        }    }    return}func userIDFromToken(c echo.Context) string {    user := c.Get("user").(*jwt.Token)    claims := user.Claims.(jwt.MapClaims)    return claims["id"].(string)}

main

最后的main.go就相对很简单了。
main.go

package mainimport (    "go_echo_examples/twitter/handler"    "github.com/labstack/echo"    "github.com/labstack/echo/middleware"    "github.com/labstack/gommon/log"    "gopkg.in/mgo.v2")func main() {    e := echo.New()    e.Logger.SetLevel(log.ERROR)    e.Use(middleware.Logger())    e.Use(middleware.JWTWithConfig(middleware.JWTConfig{        SigningKey: []byte(handler.Key),        Skipper: func(c echo.Context) bool {            // Skip authentication for and signup login requests            if c.Path() == "/login" || c.Path() == "/signup" {                return true            }            return false        },    }))    // Database connection    db, err := mgo.Dial("localhost")    if err != nil {        e.Logger.Fatal(err)    }    // Create indices    if err = db.Copy().DB("twitter").C("users").EnsureIndex(mgo.Index{        Key:    []string{"email"},        Unique: true,    }); err != nil {        log.Fatal(err)    }    // Initialize handler    h := &handler.Handler{DB: db}    // Routes    e.POST("/signup", h.Signup)    e.POST("/login", h.Login)    e.POST("/follow/:id", h.Follow)    e.POST("/posts", h.CreatePost)    e.GET("/feed", h.FetchPost)    // Start server    e.Logger.Fatal(e.Start(":1323"))}

测试

启动mongodb服务

mongod.exe --dbpath d:\mongodb_data\db

成功的话,结果:

2017-11-27T00:17:22.201-0700 I CONTROL  [initandlisten] MongoDB starting : pid=17792 port=27017 dbpath=d:\mongodb_data\db 64-bit host=LAPTOP-MNU6522J2017-11-27T00:17:22.202-0700 I CONTROL  [initandlisten] targetMinOS: Windows 7/Windows Server 2008 R22017-11-27T00:17:22.202-0700 I CONTROL  [initandlisten] db version v3.4.62017-11-27T00:17:22.203-0700 I CONTROL  [initandlisten] git version: c55eb86ef46ee7aede3b1e2a5d184a7df4bfb5b52017-11-27T00:17:22.203-0700 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.1u-fips  22 Sep 20162017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten] allocator: tcmalloc2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten] modules: none2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten] build environment:2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten]     distmod: 2008plus-ssl2017-11-27T00:17:22.205-0700 I CONTROL  [initandlisten]     distarch: x86_642017-11-27T00:17:22.205-0700 I CONTROL  [initandlisten]     target_arch: x86_642017-11-27T00:17:22.205-0700 I CONTROL  [initandlisten] options: { storage: { dbPath: "d:\mongodb_data\db" } }2017-11-27T00:17:22.261-0700 I -        [initandlisten] Detected data files in d:\mongodb_data\db created by the 'wiredTiger' storage engine, so setting the active storage engine to 'wiredTiger'.2017-11-27T00:17:22.271-0700 I STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=3540M,session_max=20000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000),checkpoint=(wait=60,log_size=2GB),statistics_log=(wait=0),2017-11-27T00:17:24.247-0700 I CONTROL  [initandlisten]2017-11-27T00:17:24.248-0700 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.2017-11-27T00:17:24.259-0700 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.2017-11-27T00:17:24.260-0700 I CONTROL  [initandlisten]2017-11-27T15:17:25.037+0800 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory 'd:/mongodb_data/db/diagnostic.data'2017-11-27T15:17:25.046+0800 I NETWORK  [thread1] waiting for connections on port 27017

运行main.go
mongodb控制台:

2017-11-27T15:23:39.223+0800 I NETWORK  [thread1] connection accepted from 127.0.0.1:51150 #2 (1 connection now open)2017-11-27T15:23:39.501+0800 I INDEX    [conn2] build index on: twitter.users properties: { v: 2, unique: true, key: { email: 1 }, name: "email_1", ns: "twitter.users" }2017-11-27T15:23:39.501+0800 I INDEX    [conn2]          building index using bulk method; build may temporarily use up to 500 megabytes of RAM2017-11-27T15:23:39.529+0800 I INDEX    [conn2] build index done.  scanned 0 total records. 0 secs2017-11-27T15:23:39.530+0800 I COMMAND  [conn2] command twitter.$cmd command: createIndexes { createIndexes: "users", indexes: [ { name: "email_1", ns: "twitter.users", key: { email: 1 }, unique: true } ] } numYields:0 reslen:113 locks:{ Global: { acquireCount: { r: 1, w: 1 } }, Database: { acquireCount: { W: 1 } }, Collection: { acquireCount: { w: 1 } } } protocol:op_query 303ms

用户注册
使用postman或是curl命令,这里使用curl命令了:

curl -X POST http://localhost:1323/signup -H "Content-Type:application/json" -d '{"email" :"wangshubo1989@126.co"m", "password":"test"}'

登录

curl -X POST http://localhost:1323/login -H "Content-Type:application/json" -d '{"email" :"wangshubo1989@126.com", "password":"test"}'

返回:

{"id":"5a1bbe92271c7c5ac875e40e","email":"wangshubo1989@126.com","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTIwMjcwOTgsImlkIjoiNWExYmJlOTIyNzFjN2M1YWM4NzVlNDBlIn0.V__5q0fipKfPhcGop1rDiOX5lFc7qSVz9bVfJ5zycvo"}

Follow用户

curl -X POST http://localhost:1323/follow/5a1bbe92271c7c5ac875e40e -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTIwMjcwOTgsImlkIjoiNWExYmJlOTIyNzFjN2M1YWM4NzVlNDBlIn0.V__5q0fipKfPhcGop1rDiOX5lFc7qSVz9bVfJ5zycvo"

发送消息(邮件)

curl -X POST http://localhost:1323/posts -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTIwMjcwOTgsImlkIjoiNWExYmJlOTIyNzFjN2M1YWM4NzVlNDBlIn0.V__5q0fipKfPhcGop1rDiOX5lFc7qSVz9bVfJ5zycvo" -H "Content-Type: application/json" -d '{"to":"58465b4ea6fe886d3215c6df","message":"hello"}'

这里写图片描述

阅读全文
1 0
原创粉丝点击