前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Golang&MongoDB快速构建RESTful服务

基于Golang&MongoDB快速构建RESTful服务

原创
作者头像
JimmyDeng
发布2019-07-02 19:35:40
8400
发布2019-07-02 19:35:40
举报
文章被收录于专栏:云架构分享云架构分享

近年来,“微服务”在软件架构出现频次越来越高,其思想主要是指将一个大型的单个应用服务拆分为多个微服务,每个微服务在其自己的进程中运行,并采用轻量级的协议进程通信,通常采用的方法是基于HTTP的RESTful API。本文主要介绍一个RESTful框架的使用方式及其主要实现原理,主要基于Golang和MongoDB实现,协议采用HTTP+JSON,另外搭配ElasticSearch可以实现数据资源的搜索功能。使用者只需要定义好数据资源的结构体,即可快速构建RESTful服务。

1. 代码地址

https://github.com/jimdn/restful

2. 主要特性

  • 定义好数据资源的结构体(包含json和bson2个tags),即可实现HTTP+JSON的CURD服务,并支持过滤、范围、搜索、排序、截取等条件查询,协议如下:

HTTP方法

路径

URL参数

HTTP Body

说明

POST

/{biz}

-

新增的数据内容

新增一条数据

PUT

/{biz}/{id}

-

新增/覆盖的数据内容

新增或覆盖更新一条数据

PATCH

/{biz}/{id}

seq

修改的数据内容

部分更新一条数据(需要带上原数据的seq)

DELETE

/{biz}/{id}

-

-

删除一条数据

GET

/{biz}/{id}

-

-

查询一条数据

GET

/{biz}

page size filter range in nin all search order select

-

分页查询URL各参数示例: page=1 size=10 filter={"star":5, "city":"shenzhen"} range={"age":{"gt":20, "lt":40}} in={"color":["blue", "red"]} nin={"color":["blue", "red"]} all={"color":["blue", "red"]} search=hello order=["+age", "-time"] select=["id", "name", "age"]

  • 定义数据资源结构体时,支持的数据类型包含:

· 普通类型: bool int32 uint32 int64 uint64 float32 float64 string struct · 数组类型: []bool []int32 []uint32 []int64 []uint64 []float32 []float64 []string []struct · 字典类型: map[string]bool map[string]int32 map[string]uint32 map[string]int64 map[string]uint64 map[string]float32 map[string]float64 map[string]string map[string]struct

  • 支持字段级别的只创建、只读配置:

· CreateOnly: 只允许创建,不允许后续修改字段 · ReadOnly: 只允许读取字段,不允许创建和修改,适用于从别的系统导入数据到数据库,然后提供数据的读取服务

  • 具备字段检查功能,传入的数据资源字段类型出错或者不存在,会返回失败并提示具体错误信息。
  • 支持用户传入数据ID或自动创建ID,自动创建ID采用UUIDv4字符串格式,定义数据资源结构体需要固定定义1个id字段,需要注意tags的写法:
代码语言:javascript
复制
type Foo struct {
    Id  *string  `json:"id,omitempty" bson:"_id,omitempty"`
     ...
}
  • 支持跟踪数据的创建和修改时间,定义数据资源结构体需要额外定义2个字段,分别为:

· btime: birth time,记录该条数据创建的时间戳 · mtime: modify time, 记录该条数据最后一次修改的时间戳

  • 支持防并发写,定义数据资源结构体需要额外定义1个seq字段:

· seq: 数据序列号,数据每次被修改都会更新序列号,更新(PATCH)请求需要带上数据原seq防止并发写引起数据错乱

  • 支持自定义传入数据库名称和表名称(集合名称),只需在URL参数里传入:

· db: 数据库名称 · col: 表名称(集合名称) 示例:/{Biz}?db=dbName&col=colName 如未指定,则组件默认以rest_{biz}作为数据库名称,以cn作为表名称

3. 代码示例

框架使用方式非常简单,初始化好一个router路由句柄和mongodb句柄,定义好数据资源的结构,即可服务。

服务前需要先启动一个MongoDB服务,假设服务地址为:mongodb://127.0.0.1:27017

代码语言:javascript
复制
package main

import (
   "fmt"
   "net/http"
   "time"
   "github.com/globalsign/mgo"
   "github.com/gorilla/mux"
   "github.com/jimdn/restful"
)

// step 1: init data structure
type Student struct {
    Id        *string   `json:"id,omitempty" bson:"_id,omitempty"`
    Name      *string   `json:"name,omitempty" bson:"name,omitempty"`
    Age       *int64    `json:"age,omitempty" bson:"age,omitempty"`
    Sex       *string   `json:"sex,omitempty" bson:"sex,omitempty"`
    Hobbies   []string  `json:"hobbies,omitempty" bson:"hobbies,omitempty"`
    Btime     *int64    `json:"btime,omitempty" bson:"btime,omitempty"`   // internal, doc birth time
    Mtime     *int64    `json:"mtime,omitempty" bson:"mtime,omitempty"`   // internal, doc modify time
    Seq       *string   `json:"seq,omitempty" bson:"seq,omitempty"`       // internal, doc seq, prevent concurrent updating
}

func main () {
    // step 2: init router and mongodb
    router := mux.NewRouter()
    mgoSess, err := mgo.DialWithTimeout("mongodb://127.0.0.1:27017", 5 * time.Second)
    if err != nil {
        fmt.Printf("mongo dial err: %v\n", err)
        return
    }

    // step 3: init restful
    processors := []restful.Processor {
        {
            Biz:          "students",
            URLPath:      "/students",
            DataStruct:   new(Student),
        },
    }
    restfulGlobalCfg := restful.GlobalConfig {
        Mux: router,
        MgoSess: mgoSess,
    }
    err = restful.Init(&restfulGlobalCfg, &processors)
    if err != nil {
        fmt.Printf("restful init err: %v\n", err)
        return
    }

    // step4: start http server
    srv := &http.Server{
        Handler:      router,
        Addr:         "127.0.0.1:8080",
        WriteTimeout: 10 * time.Second,
        ReadTimeout:  10 * time.Second,
    }
    err = srv.ListenAndServe()
    if err != nil {
        fmt.Printf("ListenAndServe err: %v", err)
    }
}

更多示例可以参考代码库里的examples目录里的示例。

4. 主要实现思路

  • 字段解析组件,代码主要在field.go文件: 主要为每个定义好的数据资源结构体做字段解析,主要包含字段类型、字段只创建只读配置、搜索字段等。对该资源的CURD操作涉及到的字段,都会与解析结果做比对,不匹配的会返回失败,并提示错误字段的信息。这里的字段名,取至数据资源结构体字段tags里的json值。
  • 处理器组件,代码主要在processor.go文件: 主要为每个数据资源定义一个处理器,处理器主要存储了该数据资源的业务名{Biz},URL服务路径,字段解析结果,CURD的处理函数等。 - 业务名{Biz}: 主要用于生成默认的数据库名称,若开启了搜索功能,还用于Elasticsearch的biz字段值。 - URL服务路径: 使用者通过配置参数,可以实现自定义URL服务路径,如统一加上版本信息/v2/{Biz},统一加上前缀/cgi/{Biz}。 - 字段解析结果: 主要保存了字段解析组件对数据资源字段信息进行解析的结果,用于对传入的数据资源进行合法性校验。 - CURD的处理函数:一般使用者不需要配置,处理器组件已经实现了默认的CURD处理函数,这些函数主要处理与MongoDB的数据交互。
  • 请求处理组件,代码主要在msg.go文件: 主要实现了HTTP请求的预处理和回包处理。回包格式统一为如下格式:
代码语言:javascript
复制
    type Foo struct {
        Id  *string  `json:"id,omitempty" bson:"_id,omitempty"`
        ...
    }

如请求处理失败,失败代码会同时体现在HttpStatusCode和包体的code,失败信息会体现在包体的msg。

  • 搜索组件,代码主要在es.go文件: 主要支持分页查询的搜索功能,在数据资源初始化时,需要传入支持搜索的字段名列表。后续数据资源的POST、PUT、PATCH请求处理完成后,会创建一个OnWriteDone协程处理搜索处理的更新。在分页查询时,如果URL传入search参数,则会先请求Elasticsearch获取命中搜索词的资源ID列表,再做后续的查询。

4. 待完善功能

字段内容值的合法性判断

字段加密功能

字段脱敏功能

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 代码地址
  • 2. 主要特性
  • 3. 代码示例
  • 4. 待完善功能
相关产品与服务
云数据库 MongoDB
腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档