有没有基于beego做的API开源项目

回复

Go开源项目easonme 回复了问题 • 2 人关注 • 1 个回复 • 1161 次浏览 • 2016-10-23 14:17 • 来自相关话题

社区的通知貌似有点问题。

有问必答astaxie 回复了问题 • 2 人关注 • 1 个回复 • 460 次浏览 • 2016-10-23 10:05 • 来自相关话题

beego 静态模板路径

有问必答xieyanke 回复了问题 • 2 人关注 • 1 个回复 • 1069 次浏览 • 2016-10-23 09:08 • 来自相关话题

gocn.io开源吗?

有问必答lcp0578 回复了问题 • 2 人关注 • 1 个回复 • 417 次浏览 • 2016-10-23 00:16 • 来自相关话题

Apiware:一个轻松将net/http及fasthttp请求参数绑定到结构体的中间件

开源程序henrylee2cn 发表了文章 • 4 个评论 • 648 次浏览 • 2016-10-22 23:26 • 来自相关话题

Apiware 查看全部

Apiware GoDoc


Apiware binds the specified parameters of the Golang net/http and fasthttp requests to the structure and verifies the validity of the parameter values.


It is suggested that you can use the struct as the Handler of the web framework, and use the middleware to quickly bind the request parameters, saving a lot of parameter type conversion and validity verification. At the same time through the struct tag, create swagger json configuration file, easy to create api document services.


Apiware将Go语言net/httpfasthttp请求的指定参数绑定到结构体,并验证参数值的合法性。
建议您可以使用结构体作为web框架的Handler,并用该中间件快速绑定请求参数,节省了大量参数类型转换与有效性验证的工作。同时还可以通过该结构体标签,创建swagger的json配置文件,轻松创建api文档服务。


Demo 示例


package main

import (
"encoding/json"
"github.com/henrylee2cn/apiware"
// "mime/multipart"
"net/http"
"strings"
)

type TestApiware struct {
Id int `param:"in(path),required,desc(ID),range(1:2)"`
Num float32 `param:"in(query),name(n),range(0.1:10.19)"`
Title string `param:"in(query),nonzero"`
Paragraph []string `param:"in(query),name(p),len(1:10)" regexp:"(^[\\w]*$)"`
Cookie http.Cookie `param:"in(cookie),name(apiwareid)"`
CookieString string `param:"in(cookie),name(apiwareid)"`
// Picture multipart.FileHeader `param:"in(formData),name(pic),maxmb(30)"`
}

var myApiware = apiware.New(pathDecodeFunc, nil, nil)

var pattern = "/test/:id"

func pathDecodeFunc(urlPath, pattern string) apiware.KV {
idx := map[int]string{}
for k, v := range strings.Split(pattern, "/") {
if !strings.HasPrefix(v, ":") {
continue
}
idx[k] = v[1:]
}
pathParams := make(map[string]string, len(idx))
for k, v := range strings.Split(urlPath, "/") {
name, ok := idx[k]
if !ok {
continue
}
pathParams[name] = v
}
return apiware.Map(pathParams)
}

func testHandler(resp http.ResponseWriter, req *http.Request) {
// set cookies
http.SetCookie(resp, &http.Cookie{
Name: "apiwareid",
Value: "http_henrylee2cn",
})

// bind params
params := new(TestApiware)
err := myApiware.Bind(params, req, pattern)
b, _ := json.MarshalIndent(params, "", " ")
if err != nil {
resp.WriteHeader(http.StatusBadRequest)
resp.Write(append([]byte(err.Error()+"\n"), b...))
} else {
resp.WriteHeader(http.StatusOK)
resp.Write(b)
}
}

func main() {
// Check whether `testHandler` meet the requirements of apiware, and register it
err := myApiware.Register(new(TestApiware))
if err != nil {
panic(err)
}

// server
http.HandleFunc("/test/0", testHandler)
http.HandleFunc("/test/1", testHandler)
http.HandleFunc("/test/1.1", testHandler)
http.HandleFunc("/test/2", testHandler)
http.HandleFunc("/test/3", testHandler)
http.ListenAndServe(":8080", nil)
}

Struct&Tag 结构体及其标签























































































































tag key required value desc
param in only one path (position of param) if required is unsetted, auto set it. e.g. url: "http://www.abc.com/a/{path}"
param in only one query (position of param) e.g. url: "http://www.abc.com/a?b={query}"
param in only one formData (position of param) e.g. "request body: a=123&b={formData}"
param in only one body (position of param) request body can be any content
param in only one header (position of param) request header info
param in only one cookie (position of param) request cookie info, support: http.Cookie,fasthttp.Cookie,string,[]byte
param name no (e.g. "id") specify request param`s name
param required no required request param is required
param desc no (e.g. "id") request param description
param len no (e.g. 3:6, 3) length range of param's value
param range no (e.g. 0:10) numerical range of param's value
param nonzero no nonzero param`s value can not be zero
param maxmb no (e.g. 32) when request Content-Type is multipart/form-data, the max memory for body.(multi-param, whichever is greater)
regexp no (e.g. "^\w+$") param value can not be null
err no (e.g. "incorrect password format") customize the prompt for validation error

NOTES:



  • the binding object must be a struct pointer

  • the binding struct's field can not be a pointer

  • regexp or param tag is only usable when param:"type(xxx)" is exist

  • if the param tag is not exist, anonymous field will be parsed

  • when the param's position(in) is formData and the field's type is multipart.FileHeader, the param receives file uploaded

  • if param's position(in) is cookie, field's type must be http.Cookie

  • param tags in(formData) and in(body) can not exist at the same time

  • there should not be more than one in(body) param tag


Field Types 结构体字段类型














































































base slice special
string []string [][]byte
byte []byte [][]uint8
uint8 []uint8 multipart.FileHeader (only for formData param)
bool []bool http.Cookie (only for net/http's cookie param)
int []int fasthttp.Cookie (only for fasthttp's cookie param)
int8 []int8 struct (struct type only for body param or as an anonymous field to extend params)
int16 []int16
int32 []int32
int64 []int64
uint8 []uint8
uint16 []uint16
uint32 []uint32
uint64 []uint64
float32 []float32
float64 []float64

Source code


https://github.com/henrylee2cn/apiware

推荐一些golang项目, 学习一下源码

技术讨论wwdyy 回复了问题 • 30 人关注 • 6 个回复 • 2584 次浏览 • 2016-10-22 22:56 • 来自相关话题

关于接口实现的一个小问题

Golangcodinghxl 回复了问题 • 9 人关注 • 5 个回复 • 1139 次浏览 • 2016-10-22 22:53 • 来自相关话题

协程无法启动

有问必答changjixiong 回复了问题 • 4 人关注 • 4 个回复 • 506 次浏览 • 2016-10-22 21:27 • 来自相关话题

encode/json 的Marshal函数对于指针和对象返回得到结果不一样

技术讨论stevewang 回复了问题 • 3 人关注 • 3 个回复 • 845 次浏览 • 2016-10-22 19:46 • 来自相关话题

大家如何看待百度的bfs?

有问必答niugou 回复了问题 • 3 人关注 • 1 个回复 • 1003 次浏览 • 2016-10-22 19:45 • 来自相关话题

求菜鸟golang开发学习建议

有问必答changjixiong 回复了问题 • 3 人关注 • 3 个回复 • 649 次浏览 • 2016-10-22 19:11 • 来自相关话题

Gear: 一个 Go web framework 的设计思考和讨论

开源程序zensh 发表了文章 • 3 个评论 • 1180 次浏览 • 2016-10-22 16:57 • 来自相关话题

Gear 框架设计考量

Gear 是由 Teambition查看全部

Gear 框架设计考量


Gear 是由 Teambition 开发的一个轻量级的、专注于可组合扩展和高性能的 Go 语言 Web 服务框架。


Gear 框架在设计与实现的过程中充分参考了 Go 语言下多款知名 Web 框架,也参考了 Node.js 下的知名 Web 框架,汲取各方优秀因素,结合我们的开发实践,精心打磨而成。Gear 框架主要有如下特点:



  1. 基于中间件模式的业务处理控制流程。中间件模式使功能模块开发标准化、解耦、易于组合和集成到应用

  2. 框架级的错误和异常自动处理机制。开发者无需再担心业务逻辑中的每一个错误,只需在中间件返回错误,交给框架自动处理,也支持自定义处理逻辑

  3. 集成了便捷的读写 HTTP Request/Response 对象的方法,使得 Web 应用开发更加高效

  4. 高效而强大的路由处理器,能定义出各种路由规则满足业务逻辑需求

  5. 丰富的中间件生态,如 CORS, CSRF, Secure, Logging, Favicon, Session, Rate limiter, Tracing等

  6. 完整的 HTTP/2.0 支持

  7. 超轻量级,框架只实现核心的、共性的需求,可选需求均通过外部中间件或库来满足,确保应用实现的灵活自由,不被框架绑定束缚


目前 Gear 已经发布 v1.0.0


《Gear 框架设计考量》


2017-03-05 更新




2016-10-22


起因


因公司技术转型需要(见招聘贴 https://gocn.io/question/36 ),我最近一个月都在学习 Go 语言,研究 Go 语言下各种 Web 框架,主要有以下三个印象:



  1. Go 语言原生的 net/http 效率很低,第三方实现的 fasthttp 效率极高,是原生 10 倍以上,封神榜上排名第二。

  2. Go 的 Web 框架非常多,但知名度高的框架基本上是基于 fasthttp,除了 Asta谢 的 Beego

  3. Web 框架不但多,很多看起来也相似,但各自都有自己的功能插件和生态,API也非常繁杂。这大概是强类型引发的问题,很难像 Node.js 生态圈那样共用他人的开源模块。


我在 Node.js 生态圈中做了一个 Web 框架:https://github.com/toajs/toa ,我把它搬到了 Go 生态,这就是 Gear: https://github.com/teambition/gear


Gear 特性和目标



  1. 充分利用 Go 语言原生接口,一方面原生实现总是在不断进步优化;另一方面通用性更好,这点对于强类型来说很重要,尽量解耦类型依赖。

  2. 轻量级,框架本身只提供开发完整 Web 服务需要的核心功能,其它功能均通过中间件扩展。

  3. 充分发挥和扩展 Go 建议使用的 context.Context 能力。


关于性能


看过了太多的 benchmark,我以为 net/http 真弱鸡,但其实不然,在我的 14寸 rMBP 的测试结果如下:


Gear 48307 vs Iris 70310


Gear with "net/http": 48307


> wrk 'http://localhost:3333/?foo[bar]=baz' -d 10 -c 100 -t 4

Running 10s test @ http://localhost:3333/?foo[bar]=baz
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.30ms 2.53ms 59.54ms 94.28%
Req/Sec 12.15k 1.56k 20.98k 81.75%
484231 requests in 10.02s, 63.27MB read
Requests/sec: 48307.40
Transfer/sec: 6.31MB

Iris with "fasthttp": 70310


> wrk 'http://localhost:3333/?foo[bar]=baz' -d 10 -c 100 -t 4

Running 10s test @ http://localhost:3333/?foo[bar]=baz
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.37ms 648.31us 15.60ms 89.48%
Req/Sec 17.75k 2.32k 39.65k 84.83%
710317 requests in 10.10s, 102.29MB read
Requests/sec: 70310.19
Transfer/sec: 10.13MB

目前 Gear 还只刚成型,没有开始优化,另外据说 Go 1.8 的 net/http 有很大优化,两项相加赶上 Iris 完全可能。

高可用架构·Learning as we Go(第5期)

文章分享itfanr 发表了文章 • 1 个评论 • 472 次浏览 • 2016-10-22 15:04 • 来自相关话题

go语言实战向导

开源程序simplejia 发表了文章 • 2 个评论 • 671 次浏览 • 2016-10-22 14:16 • 来自相关话题

导语: 用golang实现后台服务,你想要的都在这儿,有webserver最简框架(wsp),有交互式编程环境(查看全部


导语: 用golang实现后台服务,你想要的都在这儿,有webserver最简框架(wsp),有交互式编程环境(gop),有进程管理服务(cmonitor),有orm(orm),有本地kv缓存(lc),等等





使用go语言做后台服务已经有3年了,通过项目去检验一个又一个的想法,然后不断总结,优化,最终形成了自己的一整套体系,小到一个打印对象的方法,大到一个web后台项目最佳实践指导,这一点一滴都是在不断的实践中进化开来,以下内容是其中一小部分的汇报,各位看官如有兴致,请前往 https://github.com/simplejia 关注最新的代码变更,希望和golang爱好者们共同探讨,也感谢xie大提供这样一个优秀的社区场所。


wsp (go http webserver)


实现初衷



  • 简单可依赖,充分利用go已有的东西,不另外增加复杂、难以理解的东西,这样做的好处包括:更容易跟随go的升级而升级,降低使用者学习成本

  • yii提供的controller/action的路由方式比较常用,在wsp里实现一套

  • java annotation的功能挺方便,在wsp里,通过注释来实现过滤器方法的调用定义

  • 不能因为wsp的引入而降低原生go http webserver的性能


使用场景



  • 以http webserver方式对外提供服务

  • 后台接口服务


使用案例



  • 大型互联网社交业务


实现方式



  • 路由自动生成,按要求(见demo/controller/demo.go)提供controller/action的实现代码,wsp执行后会分析项目代码,自动生成路由表并记录在文件demo/WSP.go里,controller/action定义代码必须符合函数定义:func(http.ResponseWriter, *http.Request),并且是带receiver的method
    demo_set.go


package controller

import (
"net/http"

"github.com/simplejia/wsp/demo/service"
)

// @prefilter("Login", {"Method":{"type":"get"}})
// @postfilter("Boss")
func (demo *Demo) Set(w http.ResponseWriter, r *http.Request) {
key := r.FormValue("key")
value := r.FormValue("value")
demoService := service.NewDemo()
demoService.Set(key, value)

json.NewEncoder(w).Encode(map[string]interface{}{
"code": 0,
})
}

WSP.go


// generated by wsp, DO NOT EDIT.

package main

import "net/http"
import "time"
import "github.com/simplejia/wsp/demo/controller"
import "github.com/simplejia/wsp/demo/filter"

func init() {
http.HandleFunc("/Demo/Get", func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
_ = t
var e interface{}
c := new(controller.Demo)
defer func() {
e = recover()
if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
}()
c.Get(w, r)
})

http.HandleFunc("/Demo/Set", func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
_ = t
var e interface{}
c := new(controller.Demo)
defer func() {
e = recover()
if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
}()
if ok := filter.Login(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
if ok := filter.Method(w, r, map[string]interface{}{"type": "get", "__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
c.Set(w, r)
})

}


  • wsp分析项目代码,寻找符合要求的注释(见demo/controller/demo_set.go),自动生成过滤器调用代码在文件demo/WSP.go里,filter注解分为前置过滤器(prefilter)和后置过滤器(postfilter),格式如:@prefilter({json body}),{json body}代表传入参数,符合json array定义格式(去掉前后的中括号),可以包含string值或者object值,filter函数定义满足:func (http.ResponseWriter, *http.Request, map[string]interface{}) bool,过滤器函数如下:
    method.go


package filter

import (
"net/http"
"strings"
)

func Method(w http.ResponseWriter, r *http.Request, p map[string]interface{}) bool {
method, ok := p["type"].(string)
if ok && strings.ToLower(r.Method) != strings.ToLower(method) {
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
return false
}
return true
}


filter输入参数map[string]interface{},会自动设置"T",time.Time类型,值为执行起始时间,可用于耗时统计,"C",{Controller}类型,值为{Controller}实例,可通过接口方式存取相关数据(这种方式存取数据较context方式更简单实用),"E",值为recover()返回值,用于检测错误并处理(后置过滤器必须recover())




  • 项目main.go代码示例
    main.go


package main

import (
"log"

"github.com/simplejia/clog"
"github.com/simplejia/lc"

"net/http"

_ "github.com/simplejia/wsp/demo/clog"
_ "github.com/simplejia/wsp/demo/conf"
_ "github.com/simplejia/wsp/demo/mysql"
_ "github.com/simplejia/wsp/demo/redis"
)

func init() {
lc.Init(1e5)

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
})
}

func main() {
clog.Info("main()")

log.Panic(http.ListenAndServe(":8080", nil))
}

miscellaneous



  • 通过wrk压测工具在同样环境下(8核,8g),wsp空跑qps:9万,beego1.7.1空跑qps:5.5万

  • 更方便加入middleware(func(http.Handler) http.Handler),其实更推荐通过定义过滤器的方式支持类似功能

  • 更方便编写如下的测试用例:




demo



  • 提供一个简单易扩展的项目stub


实现初衷



  • 简单可依赖,充分利用go已有的东西,不另外增加复杂、难以理解的东西,这样做的好处包括:更容易跟随go的升级而升级,降低使用者学习成本

  • 提供常用组件的简单包装,如下:

    • config,提供项目主配置文件自动解析,见conf

    • redis,使用(github.com/garyburd/redigo),提供配置文件自动解析,见redis

    • mysql,使用(database/sql),提供配置文件自动解析,见mysql,同时为了方便对象映射,提供了最常用的orm组件供选择使用,见orm



项目编写指导意见



  • 目录结构:
    ├── WSP.go
    ├── clog
    │ └── clog.go
    ├── conf
    │ ├── conf.go
    │ └── conf.json
    ├── controller
    │ ├── base.go
    │ ├── demo.go
    │ ├── demo_get.go
    │ └── demo_set.go
    ├── demo
    ├── filter
    │ ├── boss.go
    │ ├── login.go
    │ └── method.go
    ├── main.go
    ├── model
    │ ├── demo.go
    │ ├── demo_get.go
    │ └── demo_set.go
    ├── mysql
    │ ├── demo_db.json
    │ └── mysql.go
    ├── redis
    │ ├── demo.json
    │ └── redis.go
    ├── service
    │ ├── demo.go
    │ ├── demo_get.go
    │ └── demo_set.go
    └── test
    ├── clog -> ../clog
    ├── conf -> ../conf
    ├── demo_get_test.go
    ├── demo_set_test.go
    ├── init_test.go
    ├── mysql -> ../mysql
    └── redis -> ../redis


    • controller目录:负责request参数解析,service调用

    • service目录:负责逻辑处理,model调用

    • model目录:负责数据处理


  • 接口实现上,建议一个接口对应一个文件,如controller/demo_get.go, service/demo_get.go, model/demo_get.go

回复的时候,空行会被删除

站点反馈itfanr 回复了问题 • 3 人关注 • 5 个回复 • 601 次浏览 • 2016-10-22 11:25 • 来自相关话题