加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Go实战--golang中使用echo框架、MongoDB、JWT搭建REST API(labst

发布时间:2020-12-16 09:42:17 所属栏目:大数据 来源:网络整理
导读:生命不止,继续go go go !!! 之前介绍过golang中restful api的博客,是使用redis作为持久化,httprouter作为框架: Go实战–通过httprouter和redis框架搭建restful api服务(github.com/julienschmidt/httprouter) 今天,继续echo框架,这次加入mongodb作为持

生命不止,继续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.go main.go

model

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

user.go

package model

import (
    "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 model

import (
    "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 handler

import (
    "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 handler

import (
    "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 handler

import (
    "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 main

import (
    "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_datadb

成功的话,结果:

2017-11-27T00:17:22.201-0700 I CONTROL  [initandlisten] MongoDB starting : pid=17792 port=27017 dbpath=d:mongodb_datadb 64-bit host=LAPTOP-MNU6522J
2017-11-27T00:17:22.202-0700 I CONTROL  [initandlisten] targetMinOS: Windows 7/Windows Server 2008 R2
2017-11-27T00:17:22.202-0700 I CONTROL  [initandlisten] db version v3.4.6
2017-11-27T00:17:22.203-0700 I CONTROL  [initandlisten] git version: c55eb86ef46ee7aede3b1e2a5d184a7df4bfb5b5
2017-11-27T00:17:22.203-0700 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.1u-fips  22 Sep 2016
2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten] allocator: tcmalloc
2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten] modules: none
2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten] build environment:
2017-11-27T00:17:22.204-0700 I CONTROL  [initandlisten]     distmod: 2008plus-ssl
2017-11-27T00:17:22.205-0700 I CONTROL  [initandlisten]     distarch: x86_64
2017-11-27T00:17:22.205-0700 I CONTROL  [initandlisten]     target_arch: x86_64
2017-11-27T00:17:22.205-0700 I CONTROL  [initandlisten] options: { storage: { dbPath: "d:mongodb_datadb" } }
2017-11-27T00:17:22.261-0700 I -        [initandlisten] Detected data files in d:mongodb_datadb 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 RAM
2017-11-27T15:23:39.529+0800 I INDEX    [conn2] build index done.  scanned 0 total records. 0 secs
2017-11-27T15:23:39.530+0800 I COMMAND  [conn2] command twitter.$cmd command: createIndexes { createIndexes: "users",indexes: [ { name: "email_1",ns: "twitter.users",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"}'

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读