teambition / gear Goto Github PK
View Code? Open in Web Editor NEWA lightweight, composable and high performance web service framework for Go.
License: MIT License
A lightweight, composable and high performance web service framework for Go.
License: MIT License
app.Use(static.New(static.Options{ Root: "./public", }))
127.0.0.1 GET /static/banner.jpg/ 200 57683 - 0.000 ms
此源码是依赖go 1.8的版本么?
目前对于 SetOnError 的回调要求格式是func(ctx *Context, err HTTPError)
.
当前的预期(个人理解)是: 使用者在回调方法里处理err
并调用 ctx.End
之类的方法.
存在的问题有:
err
是 ParseError
处理后的对象, 使用者无法知道原始错误信息;ctx.Error
响应数据, 会存在死循环;将回调的格式改为 func(ctx *Context, err error) error
:
当返回新的 error 时, gear 使用新的 error 交给 ParseError
处理, 否则还是使用原err
;
这个方法并没有解决回调里调用
ctx.Error
导致的死循环问题.
带来的好处是: 用户可以拿到原始 error 并处理, 然后返回给 gear 继续处理;
当然, 这个方法是存在兼容问题的! 解决办法是: 回调函数支持两种方式
对于旧的方式, 再包装成新的格式.
func (ctx *Context, err error) error {
fn(ctx, ParseError(err, ctx.Res.status()))
return nil // 返回 nil 表示已经自行处理过
}
如果使用旧的方式, 打印一个 deprecated warning 信息.
您好,我正在使用您的 gear
框架开发一个web应用,最近想要通过 pprof
分析应用性能方面的问题,按照 pprof
文档的指导 https://golang.org/pkg/net/http/pprof/
自定义一个 pprof 的 controller,部分代码如下:
package ctl
type Pprof struct {
...
}
// Init ...
func (p *Pprof) Get(ctx *gear.Context) error {
switch ctx.Param("pp") {
default:
pprof.Index(ctx.Res.w, ctx.Req)
case "":
pprof.Index(ctx.Res.w, ctx.Req)
case "cmdline":
pprof.Cmdline(ctx.Res.w, ctx.Req)
case "profile":
pprof.Profile(ctx.Res.w, ctx.Req)
case "symbol":
pprof.Symbol(ctx.Res.w, ctx.Req)
}
ctx.Res.WriteHeader(200)
}
但是查下源码发现 gear.Context
中 http.ResponseWriter
不能导出,所以上面代码编译不通过。
type Response struct {
status int // response Status Code
body []byte // the response content.
afterHooks []func()
endHooks []func()
ended atomicBool // indicate that app middlewares run out.
wroteHeader atomicBool
w http.ResponseWriter // the origin http.ResponseWriter, should not be override.
rw http.ResponseWriter // maybe a http.ResponseWriter wrapper
}
请问这种情况有办法处理吗?
app := gear.New()
app.Use(func(ctx *gear.Context) error {
return ctx.HTML(200, "Hello World")
})
listener, _ := net.Listen("tcp", ":9090")
app.Server.Serve(listener)
It won't response Hello World
for any request.
I think add app.Server.Handler = app
in New()
may help. So is app.Server.ErrorLog = app.logger
.
现在客户端 POST 请求上来的数据是一些监控数据,因为数据量大所以采用了 zlib 压缩,如何在 gear 这边自动检测 header 并解压?
我看了 gear 关于中间件部分的文档和代码,想通过 middleware 的方式来实现,
package middleware
import (
"compress/zlib"
"github.com/teambition/gear"
)
// Decompress ...
func Decompress() gear.Middleware {
return func(ctx *gear.Context) error {
if ctx.GetHeader(gear.HeaderContentEncoding) == "deflate" {
var err error
ctx.Req.Body, err = zlib.NewReader(ctx.Req.Body)
if err != nil {
return err
}
}
return nil
}
}
app.go 注册这个中间件
app.Use(middleware.Decompress())
但是这样做好像有问题,日志报错413的错误
error:Error{Code:413, Err:\"RequestEntityTooLarge\", Msg:\"unexpected EOF\", Data:\u003cnil\u003e, Stack:\"\"}
希望您有空的时候帮忙看看我这个需求应该如何实现比较好。
现在如果设置一个路由 /user/:id
,然后访问 localhost:3000/user/
也是适配到 /user/:id
这个路由的,正常这种应该是适配到 /user
的吧?
right now, the "Vary: Origin" header only be set when allowOrigin is "" or not equal to "*", this will poison intermediate caches(CDN or Varnish, Squid, etc), for detail
文档是很重要的对于开源项目
rt.
gear HTTP2 & gRPC not working for http server without TLS(or maybe the new gRPC lib related issue).
We use cmux as port proxy, so we need using app.Server.Serve(lis) to work
lis, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
// Create a cmux.
m := cmux.New(l)
// Match connections in order:
// First grpc, then HTTP, and otherwise Go RPC/TCP.
grpcL := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
_ = grpcL
httpL := m.Match(cmux.HTTP1Fast())
app := gear.New()
// etc.
// but we can not inject logger because is not exposable.
app.Server.Handler = app
app.Server.Serve(httpL)
Can we expose app.Serve(lis) func?
router.Get("/hello/:name", func(ctx *gear.Context) error {
return ctx.HTML(200, "<h1>Hello, Gear!</h1>")
})
RT.
项目依赖建议
package main
import (
"github.com/teambition/gear"
"github.com/teambition/gear/logging"
)
func main(){
app := gear.New()
app.UseHandler(logging.Default(true))
router := gear.NewRouter(gear.RouterOptions{TrailingSlashRedirect :false})
router.Get("/hello/:name", func(ctx *gear.Context) error {
return ctx.HTML(200, "<h1>Hello, Gear!</h1>")
})
app.UseHandler(router)
app.Error(app.Listen(":3000"))
}
$curl curl http://localhost:3000/hello/gear/
127.0.0.1 - - [2021-09-10T10:35:52.391Z] "GET /hello/gear/ HTTP/1.1" 421 119 0.000ms
{"error":"MisdirectedRequest","message":"The request was directed at a server that is not able to produce a response."}
but in net/http
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/hello/gear", func (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to my website!")
})
http.ListenAndServe(":3000", nil)
}
$curl curl http://localhost:3000/hello/gear/
404 not found
现象:
panic: Header called after Handler finished
goroutine 214 [running]:
net/http.(*http2responseWriter).Header(0xc00025e0d8, 0x0)
/usr/local/go/src/net/http/h2_bundle.go:6310 +0x99
github.com/teambition/gear.(*Response).Header(0xc000262c00, 0xb62267)
/go/pkg/mod/github.com/teambition/[email protected]/response.go:85 +0x34
github.com/teambition/gear.(*Response).Get(0xc000262c00, 0xb62267, 0xc, 0x0, 0x0)
/go/pkg/mod/github.com/teambition/[email protected]/response.go:28 +0x2b
github.com/teambition/gear/logging.New.func2(0xc00024d890, 0xc0002f4630)
/go/pkg/mod/github.com/teambition/[email protected]/logging/logger.go:236 +0x690
github.com/teambition/gear/logging.(*Logger).Serve.func1()
/go/pkg/mod/github.com/teambition/[email protected]/logging/logger.go:533 +0x178
github.com/teambition/gear.runHooks(0xc00025e0e0, 0x1, 0x1)
/go/pkg/mod/github.com/teambition/[email protected]/response.go:186 +0x39
created by github.com/teambition/gear.(*App).ServeHTTP.func1
/go/pkg/mod/github.com/teambition/[email protected]/app.go:439 +0x179
原因:
https://github.com/teambition/gear/blob/v1.15.0/logging/logger.go#L236 会从Res中读取Header值:
ctx.Res.Get(gear.HeaderXRequestID);
而Res是nil:
https://github.com/golang/net/blob/ed066c81e75eba56dd9bd2139ade88125b855585/http2/server.go#L2525
Http2在处理完请求后在 handlerDone()
中会把Res置为nil:
https://github.com/golang/net/blob/ed066c81e75eba56dd9bd2139ade88125b855585/http2/server.go#L2629
日志输出是在 OnEnd 中调用res,(OnEnd挂在end hook上,被新的goroutine执行),所以panic了。
https://github.com/teambition/gear/blob/v1.15.0/logging/logger.go#L526
func main() {
// Create app
app := gear.New()
// Create views router
viewRouter := gear.NewRouter()
viewRouter.Get("/", func(ctx *gear.Context) error {
return nil
})
// add more ...
apiRouter := gear.NewRouter(gear.RouterOptions{
Root: "/api",
IgnoreCase: true,
FixedPathRedirect: true,
TrailingSlashRedirect: true,
})
// support one more middleware
apiRouter.Get("/user/:id", func(ctx *gear.Context) error {
return nil
})
// add more ..
app.UseHandler(apiRouter) // Must add apiRouter first.
app.UseHandler(viewRouter)
// Start app at 3000
app.Listen(":3000")
}
The viewRouter
works, but apiRouter NOT.
right now, the "Vary: Origin" header only be set when allowOrigin is "" or not equal to "*", this will poison intermediate caches(CDN or Varnish, Squid, etc), for detail
找一个go web框架来学习,发现gear源码写的很有条理,很规范,支持支持
app := gear.New()
app.UseHandler(logging.Default())
Response.WriteHeader()时,异步执行end hook,并结束请求:
// execute "end hooks" with LIFO order after Response.WriteHeader.
// they run in a goroutine, in order to not block current process.
if len(r.endHooks) > 0 {
go runHooks(r.endHooks)
}
而logging 在运行时需打印Header相关信息,故有几率出现Header called after Handler finished
:
func (l *Logger) Serve(ctx *gear.Context) error {
log := l.FromCtx(ctx)
// Add a "end hook" to flush logs
ctx.OnEnd(func() {
// Ignore empty log
if len(log) == 0 {
return
}
log["Status"] = ctx.Res.Status()
log["Type"] = ctx.Res.Type()
log["Length"] = ctx.Res.Get(gear.HeaderContentLength)
l.consume(log, ctx)
})
return nil
}
ritsus-iMac:fingerprint zhaoyili$ go get github.com/teambition/gear
# github.com/teambition/gear src/github.com/teambition/gear/app.go:343: undefined: http.ErrAbortHandler src/github.com/teambition/gear/app.go:386: app.Server.Shutdown undefined (type *http.Server has no field or method Shutdown) src/github.com/teambition/gear/app.go:388: app.Server.Close undefined (type *http.Server has no field or method Close) src/github.com/teambition/gear/response.go:169: undefined: http.PushOptions src/github.com/teambition/gear/response.go:170: undefined: http.Pusher src/github.com/teambition/gear/util.go:354: undefined: url.PathEscape
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.