__Updated September 14, 2020__ ( ๐ v2 benchmark results )
Dear Gophers, after long discussions in our , we decided to release
v2.0.0 on 15 September 2020. We will summarize our current API proposal in this post, feedback is more than welcome.
โ Breaking Changes
-
// func(c *fiber.Ctx) {}
func(c *fiber.Ctx) error {}
```go
app.Use(func(c *fiber.Ctx) error {
if c.Get("x-john") == "doe" {
return c.Next()
}
return c.SendStatus(fiber.StatusForbidden)
})
app.Use("/teapot", func(c *fiber.Ctx) error {
return c.Send([]byte("Helllo, Teapot โ!"))
})
app.Get("/world", func(c *fiber.Ctx) error {
return c.SendString("Hello, World ๐!")
})
app.Get("/redirect", func(c *fiber.Ctx) error {
return c.Redirect("https://google.com")
})
- ***fiber.New()*** takes an optional ***Config*** struct as value that will replace the ***\*Settings*** pointer.
```go
// func New(settings ...*Settings) *App {}
func New(config... Config) *App {}
string addr argument.// func (app *App) Listen(address interface{}, tlsconfig ...*tls.Config) error {}
func (app *App) Listen(addr string) error {}
Prefork will be compatible with custom listeners.// func (app *App) Listener(ln net.Listener, tlsconfig ...*tls.Config) error {}
func (app *App) Listener(ln net.Listener) error {}
// func (c *Ctx) Next(err ...error) {}
func (c *Ctx) Next() error {}
// func (c *Ctx) Body() string {}
func (c *Ctx) Body() []byte {}
// func (c *Ctx) Write(bodies ...interface{}) {}
func (c *Ctx) Write(p []byte) (n int, err error) {}
*fasthttp.RequestCtx and is still compatible with the context.Context interface.// func (c *Ctx) Context() context.Context
func (c *Ctx) Context() *fasthttp.RequestCtx
// func (app *App) IsChild() bool {}
func IsChild() bool {}
๐งน Updates
-
// func (c *Ctx) Send(bodies ...interface{}) {}
func (c *Ctx) Send(body []byte) error {}
// func (c *Ctx) SendStatus(status int) {}
func (c *Ctx) SendStatus(status int) error {}
// func (c *Ctx) SendString(body string) {}
func (c *Ctx) SendString(body string) error {}
// func (c *Ctx) SendStream(stream io.Reader, size ...int) {}
func (c *Ctx) SendStream(stream io.Reader, size ...int) error {}
// func (c *Ctx) Redirect(location string, status ...int) {}
func (c *Ctx) Redirect(location string, status ...int) error {}
defaultValue argument// func (c *Ctx) FormValue(key string) string {}
func (c *Ctx) FormValue(key string, defaultValue ...string) string {}
โ Removed
-
// func (c *Ctx) SendBytes(body []byte) {}
// func (c *Ctx) Error() error {}
๐ฅ New
-
*fasthttp.Request.// c.Fasthttp.Request.Header.Peek("x-version")
c.Request().Header.Peek("x-version")
*fasthttp.Response.// c.Fasthttp.Response.Header.Peek("x-version")
c.Response().Header.Peek("x-version")
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
return c.Status(404).SendString("hi, i'm an custom error")
},
})
app.Get("/", func(c *fiber.Ctx) error {
return c.SendFile("../there/is/nothing/here")
})
c.IP() to return the value of the given header key. By default c.IP() will return the Remote IP from the tcp connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-*_.app := fiber.New(fiber.Config{
ProxyHeader: "CF-Connecting-IP",
})
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString(c.IP()) // value from CF-Connecting-IP header
})
ErrMethodNotAllowed to the error handler. The request size is limited by ReadBufferSize if GETOnly is set. Server accepts all the requests by default.app := fiber.New(fiber.Config{
GETOnly: true,
})
app.Post("/", func(c *fiber.Ctx) error {
return c.SendString("This method is not allowed")
})
๐ Routing
-
// Route
app.Get("/test::param/", handler)
// GET /test:fiber/
// Route
app.Get("/shop/product/color::color/size::size", handler)
// GET /shop/product/color:blue/size:xs
// Route
app.Get("/@:name", handler)
// GET /@Fiber
// Route
app.Get("/config/+.json", func(c *fiber.Ctx) error {
ctx.Params("+1") // abc
ctx.Params("+") // abc
})
// GET /config/abc.json // match
// GET /config/.json // no match
// GET /customer/v1/cart/proxy
app.Get("/*v1*/proxy", func(c *fiber.Ctx) error {
c.Params("*") // customer/
c.Params("*1") // customer/
c.Params("*2") // /cart
})
๐ฆ Middleware
-
๐ Improvements
-
// v1.14.x
Benchmark_Ctx_JSON-16 4596667 260 ns/op 64 B/op 2 allocs/op
Benchmark_Ctx_JSON-16 4603731 259 ns/op 64 B/op 2 allocs/op
Benchmark_Ctx_JSON-16 4652700 259 ns/op 64 B/op 2 allocs/op
Benchmark_Ctx_JSON-16 4598620 259 ns/op 64 B/op 2 allocs/op
// v2.0.0
Benchmark_Ctx_JSON-16 7186842 165 ns/op 64 B/op 2 allocs/op
Benchmark_Ctx_JSON-16 7214056 164 ns/op 64 B/op 2 allocs/op
Benchmark_Ctx_JSON-16 7227295 164 ns/op 64 B/op 2 allocs/op
Benchmark_Ctx_JSON-16 7227291 165 ns/op 64 B/op 2 allocs/op
405 Method Not Allowed will be passed to the global error handler, in the old version this behavior was not controllable.v2.0.0 allows us to implement a hybrid radix tree router which will remove the remaining allocation when CaseSenstitive is disabled and increase performance drasticly. The reason why we went with the hybrid implementation is because we still want to respect the router stack order ๐ thnx @reneWerner87// v1.14.x
Benchmark_Router_NotFound-16 235243 4843 ns/op 80 B/op 2 allocs/op
Benchmark_Router_Handler-16 1721277 696 ns/op 16 B/op 1 allocs/op
Benchmark_Router_Handler_Strict_Case-16 1848582 651 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Chain-16 9300248 129 ns/op 1 B/op 1 allocs/op
Benchmark_Router_WithCompression-16 8693692 137 ns/op 1 B/op 1 allocs/op
Benchmark_Router_Next-16 1960342 619 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Handler_CaseSensitive-16 1862936 649 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Handler_Unescape-16 255260 4573 ns/op 16 B/op 1 allocs/op
Benchmark_Router_Handler_StrictRouting-16 1857168 647 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Github_API-16 3634 317628 ns/op 0 B/op 0 allocs/op
// v2.0.0
Benchmark_Router_NotFound-16 2919049 411 ns/op 48 B/op 1 allocs/op
Benchmark_Router_Handler-16 8331446 145 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Handler_Strict_Case-16 9675211 124 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Chain-16 9088828 132 ns/op 0 B/op 0 allocs/op
Benchmark_Router_WithCompression-16 9020534 133 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Next-16 14454469 81 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Handler_CaseSensitive-16 9597826 124 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Handler_Unescape-16 6059216 196 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Handler_StrictRouting-16 9753892 123 ns/op 0 B/op 0 allocs/op
Benchmark_Router_Github_API-16 5452 214098 ns/op 0 B/op 0 allocs/op
See: ( ๐ v2 benchmark results )
_To be continued...._
Since, c.Request() & c.Response() is coming, is it possible to pass custom function hook an argument inside "FastHttp FileServer PathNotFound handler ? https://github.com/gofiber/fiber/blob/0d31dc193c235560436df8ac9f64cc0f43af5a68/router.go#L285 ?
@StarMonk the following would achieve the same result.
func main() {
app := fiber.New()
app.Static("/", "./public")
app.Use(func(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).SendString("I find your lack of navigation disturbing")
})
log.Fatal(app.Listen(":3000"))
}
API is clear more, can't wait to use it
Error handling gonna be ๐ฅ
Changes look good to me.
But why is it v1.15 and not v2.0?
I'm almost certain VSCode will ask people whether it should update the dependency in go.mod. And suddenly the code doesn't compile anymore. Is this the kind of surprise that you want to make? @Fenny
You're breaking like 80% of the API. That calls for an increment of the major version.
Hey @pofl, good question!
We definitely considered moving to v2 and eventually moved away from that idea after long discussions with the Community
It's a fact that Go has a problem when it comes to the v2+ ecosystem, @donatj wrote a good article about addressing these issues:
Go Modules have a v2+ Problem
Most and if not all repo's that moved to v2+ have issues regarding this matter:
https://github.com/labstack/echo/issues/1475, https://github.com/labstack/echo/issues/1427 https://github.com/labstack/echo/issues/1382, https://github.com/labstack/echo/issues/1374 https://github.com/labstack/echo/issues/1329, https://github.com/labstack/echo/issues/1291
https://github.com/labstack/echo/issues/1272, https://github.com/labstack/echo/issues/1244, https://github.com/go-chi/chi/issues/462, https://github.com/go-chi/chi/issues/490, https://github.com/go-chi/chi/issues/381, https://github.com/go-chi/chi/issues/366, https://github.com/go-chi/chi/issues/349, https://github.com/micro/micro/issues/1223 https://github.com/micro/micro/issues/835, https://github.com/micro/micro/issues/522 https://github.com/micro/micro/issues/481, https://github.com/micro/micro/issues/273 https://github.com/goadesign/goa/issues/2567, https://github.com/goadesign/goa/issues/2511 https://github.com/goadesign/goa/issues/2488, https://github.com/goadesign/goa/issues/2320 https://github.com/goadesign/goa/issues/2181, https://github.com/goadesign/goa/issues/2111
https://github.com/goadesign/goa/issues/2106, https://github.com/goadesign/goa/issues/1925
https://github.com/emicklei/go-restful/issues/374, https://github.com/go-gorm/gorm/issues/3356
https://github.com/go-gorm/gorm/issues/3038, https://github.com/go-gorm/gorm/issues/2886
https://github.com/go-gorm/gorm/issues/3300, https://github.com/go-gorm/gorm/issues/3310 https://github.com/argoproj/argo/issues/2602, https://github.com/golang/go/issues/35732 https://github.com/tensorflow/tensorflow/issues/39307
... this is only a fraction I found searching modules in issues, but I think it's clear thatv2+ modules are not widely known and cause problems. This shows that the Go Team needs to do a better job of shining a light on this rule.
The official Go Blog tried to clear the situation up some and posted a write-up about it. Personally I found this write-up somewhat impenetrable. For how important and unusual of a requirement this is, the write-up is not accessible enough.
We think this is the right way to go at this point in time to avoid future problems, but please feel free to change my mind!
For what is worth, we try to make this transition as good as possible and stand by for anyone who needs assistance.
You can always join us on Discord if you have questions/ideas or need help with your project!
I maintain Fiber in my spare time and all the help is welcome, so any feedback or ideas are highly appreciated!
Update: Thanks for all the feedback! We are moving to v2, see: #issuecomment-691251128
Okay, I mean you run this project as you think is best. And I want to express my gratefulness for this great library.
But the way I see it is that many projects introduce v2+ without major issues. I worked with one of those, https://github.com/jackc/pgx, and it was made quite obvious to me how I should use it. Just import "github.com/jackc/pgx/v4" and all works.
I just wanted to give you my opinion on this. I think getting 5 Github issues asking about problems with v2 is the lesser issue compared to surprising people with breaking changes. But that is just my opinion. I'll shut up now. It's off my chest and I'll leave it at that. Thanks again for the great work.
I understand that module versioning is not perfect, but @pofl is correct: by introducing breaking changes within a major version, you are going to cause _more_ problems for users.
Looking through the issues that you've cherrypicked, they for the most part seem to be easily resolved, due to misunderstandings by the user on how to use v2+, or not even related to modules at all (there was one where somebody was complaining about dep not working). That's not to say there aren't legitimate issues, but most of the problems seem to be related to understanding and documentation rather than inherent failure of the system.
Please reconsider your decision. They're not perfect, but modules are the path forward, and it would be better to work with the Go team to make them better than stepping on the boat halfway (using modules, but not adhering to the backward compatibility promise).
Thanks, @pofl, and @e3b0c442 for taking the time to shine some light on this decision!
We became trending on Project introduces breaking changes to library and I'm glad to see all the opinions regarding this matter.
I'm always open to learn new things and change my decision accordingly, I always want to make the right call for the community.
After all the feedback I received in the last 6 hours, I can say that most people would mark this project as _badly maintained_ if we introduce breaking changes to v1. So moving to v2 is the right decision to make, and here is my plan to do so:
v1 branch to be able to maintain it for security issues.go.mod ) and tagged under v2.0.0I'm not sure if we can still support legacy pre-module users this way, but that's a sacrifice we have to make.
Thanks for sharing your opinions and idea's, this is what open-source is all about ๐ช
We definitely considered moving to
v2and eventually moved away from that idea after long discussions with the Community
We became trending on Project introduces breaking changes to library and I'm glad to see all the opinions regarding this matter. So moving to v2 is the right decision to make
I do not use fiber and neither do I have a preference on whether to move to v2 or make breaking changes in v1, but if you have to choose either, I would say choose the option that the current users & maintainers of fiber are more comfortable with.
it's all trade-offs.
~If you're going to use modules, and "decide" to push breaking changes without bumping the major version, you're breaking a core requirement of both semantic versioning the package management machinery, and you're creating potentially un-fixable errors for downstream consumers. It is of course technically possible to do this โ there's no way to programmatically determine if a change is or isn't backwards compatible โ but it's incredibly hostile to all of your users. In short: this isn't a decision you get to make, you need to use v2.~
_edit_: sorry ๐คฆ I didn't see the comment 5h ago
its a greating. Thank you!
Could be cool, if , we can get header with contex. E.g:
c.Header["Authorization"]
@marcuxyz we have the ctx.Get method to fetch the request header.
// Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
app.Get("/", func(c *fiber.Ctx) {
c.Get("Authorization") // "Basic YWxhZGRpbjpvcGVuc2VzYW1l"
})
Yo @Fenny I created that Reddit thread. My aim was to kick off a discussion about the v2 methods in the larger Go community. I didn't want to put you on the spot. And I hate that you have been accused of doing a bad job as the maintainer of this project.
Nevertheless I love the fact that you are so open-minded to just change direction in this matter. This project is in good hands.
Could be cool, if , we can get header with contex. E.g:
c.Header["Authorization"]@marcuxyz we have the ctx.Get method to fetch the request header.
// Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l app.Get("/", func(c *fiber.Ctx) { c.Get("Authorization") // "Basic YWxhZGRpbjpvcGVuc2VzYW1l" })
Yes... I think that using header before its a more expressive
Thanks for being so open minded about going to 2.0 after listening to the feedback from the community. This decision has increased my confidence in using and endorsing this package even further.
Man, i really can't wait for 2.0 ๐ . What timezone will the release be on, Fenny? EST? Because if so, i've got an extra one day of waiting to do then.... ๐ญ
@mikeychowy v2 is tagged https://github.com/gofiber/fiber/releases/tag/v2.0.0 ๐ฅณ
Most helpful comment
Thanks, @pofl, and @e3b0c442 for taking the time to shine some light on this decision!
We became trending on Project introduces breaking changes to library and I'm glad to see all the opinions regarding this matter.
I'm always open to learn new things and change my decision accordingly, I always want to make the right call for the community.
After all the feedback I received in the last 6 hours, I can say that most people would mark this project as _badly maintained_ if we introduce breaking changes to
v1. So moving tov2is the right decision to make, and here is my plan to do so:v1branch to be able to maintain it for security issues.go.mod) and tagged underv2.0.0I'm not sure if we can still support legacy pre-module users this way, but that's a sacrifice we have to make.
Thanks for sharing your opinions and idea's, this is what open-source is all about ๐ช