___
| / .. )))
| | . . (((
| | . ||~~~~||
| |___ . | \__/ |
\_____/ \____/ version: 0.0.1
A high performance and Light-weighted go middleware web framework which is based on fasthttp
The MIT License
If you are a node.js developer, you will find lu is quite similar in usage with connect and express, but performance better.
go get -u github.com/vincentLiuxiang/lu
go test -v -cover
ref go test for more about go test
package main
import (
"github.com/valyala/fasthttp"
"github.com/vincentLiuxiang/lu"
)
func main() {
app := lu.New()
app.Use("/", func(ctx *fasthttp.RequestCtx, next func(error)) {
ctx.SetStatusCode(200)
next(nil)
})
app.Get("/api", func(ctx *fasthttp.RequestCtx, next func(error)) {
next(errors.New("something error occour\n"))
})
app.Use("/test", func(ctx *fasthttp.RequestCtx, next func(error)) {
ctx.SetBody([]byte("hello world\n"))
})
app.Use("/", func(err error, ctx *fasthttp.RequestCtx, next func(error)) {
ctx.SetBody([]byte(err.Error()))
})
app.Listen(":8080")
}
result
http://localhost:8080/test
200
hello world
http://localhost:8080/api
200
something error occour
app := lu.New()
app.Use("/", func(ctx *fasthttp.RequestCtx, next func(error)) {
ctx.SetBody([]byte("helloworld"))
})
app.Use("/", func(ctx *fasthttp.RequestCtx, next func(error)) {
ctx.SetBody([]byte("my name is go"))
})
server := &fasthttp.Server{
Handler: app.Handler,
Concurrency: 1024 * 1024,
}
server.ListenAndServe(":8080")
The first parameter of app.Use
we call it router, and the second parameter we call it middleware, all the middlewares will be pushed to a stack
inner lu.
when a http request comes, lu will compare ctx.Path() with []byte(router), The compare rules are below:
-
if ctx.Path() equals to []byte(router) , it matches.
-
if ctx.Path() starts with []byte(router), and len(ctx.Path()) > len(router) , and ctx.Path()[len(router)] is '/' or '?', it matches.
-
if the router is "/",it means this router matches any http request
http request will execute each middleware one-by-one until a middleware does not call next() within it.
app.Use("/hello",func(ctx *fasthttp.RequestCtx, next func(error)){
// ctx.Path() is starts with []byte("/hello"),
// if len(ctx.Path()) > len("/hello") , ctx.Path()[len("/hello")] must be '/' or '?'
next(nil)
})
app.Use("/world",func(ctx *fasthttp.RequestCtx, next func(error)){
// ctx.Path() is starts with []byte("/world"),
// if len(ctx.Path()) > len("/world") , ctx.Path()[len("/world")] must be '/' or '?'
next(nil)
})
- for example
app.Use("/", ...)
app.Use("/api", ...)
app.Use("/test", ...)
http://xxxx:xxx/test
match "/", "/test"
http://xxxx:xxx/test?xxx=xxx
match "/", "/test"
http://xxxx:xxx/test/hello
match "/", "/test"
http://xxxx:xxx/api
match "/", "/api"
http://xxxx:xxx/api/hello?xxx=xxx
match "/", "/api"
The second parameter of app.Use
method, can be to two different type
-
func(ctx *fasthttp.RequestCtx, next func(error)), we call it non-error-middleware
-
func(err error, ctx *fasthttp.RequestCtx, next func(error)), we call it error-middleware, only execute by call next(error) within a middleware
In lu, there are two stack arrays to store the midllewares which are accepted by app.Use. One is used to store non-error-middleware and the other one is used to store error-middleware
No matter in which type of middleware, when it calls next(nil), next non-error-middleware will be excute if router match. But, when calls next(errors.New('some error')) , the program will skip all the non-error-middleware and directly execute the left first error-middleware if router match.
- for example
app.Use("/",func(ctx *fasthttp.RequestCtx, next func(error)){
next(nil)
})
app.Use("/",func(ctx *fasthttp.RequestCtx, next func(error)){
next(errors.New("skip next non-error-middleware"))
})
app.Use("/",func(ctx *fasthttp.RequestCtx, next func(error)){
fmt.Println("skip this non-error-middleware")
})
app.Use("/",func(err error, ctx *fasthttp.RequestCtx, next func(error)){
fmt.Println(err.Error())
})
result:
skip next non-error-middleware
- If an incoming http request doesn't match any router, lu will response a 404 statusCode and a "Not Found" string body.
app.Use("/foo",func(ctx *fasthttp.RequestCtx, next func(error)){
// no response
})
app.Use("/bar",func(ctx *fasthttp.RequestCtx, next func(error)){
})
miss all of the middlewares
http://xxxx:xxx/go
404
Not Found
- If an incoming http request match some routers, but all the matched middleware don't response to the client, lu will response a 200 statusCode and a "" string body (fasthttp default mechanism)
app.Use("/foo",func(ctx *fasthttp.RequestCtx, next func(error)){
// no response
})
app.Use("/bar",func(ctx *fasthttp.RequestCtx, next func(error)){
})
match /foo
http://xxxx:xxx/foo
200
- In the last middleware, no matter what type of the middleware, if you call
next
, lu will response a 404 statusCode and a "Not Found" string body. Because, there is no middleware after the last middleware.
app.Use("/foo",func(ctx *fasthttp.RequestCtx, next func(error)){
// no response
})
app.Use("/bar",func(ctx *fasthttp.RequestCtx, next func(error)){
ctx.SetStatusCode(200)
ctx.SetBody([]byte("helloworld"))
next(nil)
// or
// next(errors.New("..."))
})
lu will ResetBody() and SetStatusCode(404) , SetBody("Not Found")
http://xxxx:xxx/bar
// not
// 200
// helloworld
404
Not Found
app. Finally
. However, if you callnext(nil)
ornext(error)
in the last middleware. lu providers a Finally function, allow user to custom the response
app := New()
app.Use("/test", func(ctx *fasthttp.RequestCtx, next func(error)) {
next(errors.New("error"))
})
app.Use("/", func(err error, ctx *fasthttp.RequestCtx, next func(error)) {
ctx.SetStatusCode(302)
ctx.SetBody([]byte("hello world"))
next(errors.New("finally handle"))
})
app.Finally = func(err error, ctx *fasthttp.RequestCtx) {
if err != nil {
ctx.SetStatusCode(500)
ctx.SetBody([]byte(err.Error()))
return
}
ctx.SetStatusCode(200)
ctx.SetBody([]byte("hello world"))
}
app.Listen(":3005")
lu will SetStatusCode(500) , ctx.SetBody([]byte(err.Error()))
http://xxxx:xxx/test
500
finally handle
- static lu static file serving middleware, based on fasthttp.FS.
-
app.Finally func(err error, ctx *fasthttp.RequestCtx)
-
app.Handler fasthttp.RequestHandler
-
app.Use(router string, func(ctx *fasthttp.RequestCtx, next func(error)) register non-error-middleware
-
app.Use(router string, func(err error, ctx *fasthttp.RequestCtx, next func(error))) register error-middleware
-
app.Listen(port string) listen a port. app.Listen(":8080")
-
app.Get(router string, func(ctx *fasthttp.RequestCtx, next func(error))) quite similar with app.Use but only handle http GET method
-
app.Post(router string, func(ctx *fasthttp.RequestCtx, next func(error))) only handle http POST method
-
app.Put(router string, func(ctx *fasthttp.RequestCtx, next func(error))) only handle http PUT method
-
app.Head(router string, func(ctx *fasthttp.RequestCtx, next func(error))) only handle http HEAD method
-
app.Patch(router string, func(ctx *fasthttp.RequestCtx, next func(error))) only handle http PATCH method
-
app.Delete(router string, func(ctx *fasthttp.RequestCtx, next func(error))) only handle http DELETE method
-
app.Options(router string, func(ctx *fasthttp.RequestCtx, next func(error))) only handle http OPTIONS method