From d547d441a39d33d1b91e2ea912dd622837ebc5dd Mon Sep 17 00:00:00 2001 From: Fenny Date: Tue, 3 Mar 2020 12:21:34 -0500 Subject: [PATCH 1/7] New internal router alpha --- app.go | 24 +- app_test.go | 104 ++++---- context.go | 17 +- group.go | 47 +++- router.go | 697 +++++++++++++++++++++++++--------------------------- 5 files changed, 438 insertions(+), 451 deletions(-) diff --git a/app.go b/app.go index 1f38994446..739e044d20 100644 --- a/app.go +++ b/app.go @@ -138,7 +138,7 @@ func New(settings ...*Settings) (app *App) { // Group : https://fiber.wiki/application#group func (app *App) Group(prefix string, handlers ...func(*Ctx)) *Group { if len(handlers) > 0 { - app.registerMethod("USE", prefix, "", handlers...) + app.registerMethod("USE", prefix, handlers...) } return &Group{ prefix: prefix, @@ -166,67 +166,67 @@ func (app *App) Use(args ...interface{}) *App { log.Fatalf("Invalid handler: %v", reflect.TypeOf(arg)) } } - app.registerMethod("USE", "", path, handlers...) + app.registerMethod("USE", path, handlers...) return app } // Connect : https://fiber.wiki/application#http-methods func (app *App) Connect(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodConnect, "", path, handlers...) + app.registerMethod(http.MethodConnect, path, handlers...) return app } // Put : https://fiber.wiki/application#http-methods func (app *App) Put(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodPut, "", path, handlers...) + app.registerMethod(http.MethodPut, path, handlers...) return app } // Post : https://fiber.wiki/application#http-methods func (app *App) Post(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodPost, "", path, handlers...) + app.registerMethod(http.MethodPost, path, handlers...) return app } // Delete : https://fiber.wiki/application#http-methods func (app *App) Delete(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodDelete, "", path, handlers...) + app.registerMethod(http.MethodDelete, path, handlers...) return app } // Head : https://fiber.wiki/application#http-methods func (app *App) Head(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodHead, "", path, handlers...) + app.registerMethod(http.MethodHead, path, handlers...) return app } // Patch : https://fiber.wiki/application#http-methods func (app *App) Patch(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodPatch, "", path, handlers...) + app.registerMethod(http.MethodPatch, path, handlers...) return app } // Options : https://fiber.wiki/application#http-methods func (app *App) Options(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodOptions, "", path, handlers...) + app.registerMethod(http.MethodOptions, path, handlers...) return app } // Trace : https://fiber.wiki/application#http-methods func (app *App) Trace(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodTrace, "", path, handlers...) + app.registerMethod(http.MethodTrace, path, handlers...) return app } // Get : https://fiber.wiki/application#http-methods func (app *App) Get(path string, handlers ...func(*Ctx)) *App { - app.registerMethod(http.MethodGet, "", path, handlers...) + app.registerMethod(http.MethodGet, path, handlers...) return app } // All : https://fiber.wiki/application#http-methods func (app *App) All(path string, handlers ...func(*Ctx)) *App { - app.registerMethod("ALL", "", path, handlers...) + app.registerMethod("ALL", path, handlers...) return app } diff --git a/app_test.go b/app_test.go index 43807f149f..9b29b25105 100644 --- a/app_test.go +++ b/app_test.go @@ -66,58 +66,58 @@ func Test_Methods(t *testing.T) { } -func Test_Static(t *testing.T) { - app := New() - grp := app.Group("/v1") - grp.Static("/v2", ".travis.yml") - app.Static("/yesyes*", ".github/FUNDING.yml") - app.Static("./.github") - app.Static("/john", "./.github") - req, _ := http.NewRequest("GET", "/stale.yml", nil) - resp, err := app.Test(req) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } - if resp.StatusCode != 200 { - t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) - } - if resp.Header.Get("Content-Length") == "" { - t.Fatalf(`%s: Missing Content-Length`, t.Name()) - } - req, _ = http.NewRequest("GET", "/yesyes/john/doe", nil) - resp, err = app.Test(req) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } - if resp.StatusCode != 200 { - t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) - } - if resp.Header.Get("Content-Length") == "" { - t.Fatalf(`%s: Missing Content-Length`, t.Name()) - } - req, _ = http.NewRequest("GET", "/john/stale.yml", nil) - resp, err = app.Test(req) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } - if resp.StatusCode != 200 { - t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) - } - if resp.Header.Get("Content-Length") == "" { - t.Fatalf(`%s: Missing Content-Length`, t.Name()) - } - req, _ = http.NewRequest("GET", "/v1/v2", nil) - resp, err = app.Test(req) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } - if resp.StatusCode != 200 { - t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) - } - if resp.Header.Get("Content-Length") == "" { - t.Fatalf(`%s: Missing Content-Length`, t.Name()) - } -} +// func Test_Static(t *testing.T) { +// app := New() +// grp := app.Group("/v1") +// grp.Static("/v2", ".travis.yml") +// app.Static("/yesyes*", ".github/FUNDING.yml") +// app.Static("./.github") +// app.Static("/john", "./.github") +// req, _ := http.NewRequest("GET", "/stale.yml", nil) +// resp, err := app.Test(req) +// if err != nil { +// t.Fatalf(`%s: %s`, t.Name(), err) +// } +// if resp.StatusCode != 200 { +// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) +// } +// if resp.Header.Get("Content-Length") == "" { +// t.Fatalf(`%s: Missing Content-Length`, t.Name()) +// } +// req, _ = http.NewRequest("GET", "/yesyes/john/doe", nil) +// resp, err = app.Test(req) +// if err != nil { +// t.Fatalf(`%s: %s`, t.Name(), err) +// } +// if resp.StatusCode != 200 { +// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) +// } +// if resp.Header.Get("Content-Length") == "" { +// t.Fatalf(`%s: Missing Content-Length`, t.Name()) +// } +// req, _ = http.NewRequest("GET", "/john/stale.yml", nil) +// resp, err = app.Test(req) +// if err != nil { +// t.Fatalf(`%s: %s`, t.Name(), err) +// } +// if resp.StatusCode != 200 { +// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) +// } +// if resp.Header.Get("Content-Length") == "" { +// t.Fatalf(`%s: Missing Content-Length`, t.Name()) +// } +// req, _ = http.NewRequest("GET", "/v1/v2", nil) +// resp, err = app.Test(req) +// if err != nil { +// t.Fatalf(`%s: %s`, t.Name(), err) +// } +// if resp.StatusCode != 200 { +// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) +// } +// if resp.Header.Get("Content-Length") == "" { +// t.Fatalf(`%s: Missing Content-Length`, t.Name()) +// } +// } func Test_Group(t *testing.T) { app := New() diff --git a/context.go b/context.go index 676a69bfd9..cb2975eb54 100644 --- a/context.go +++ b/context.go @@ -30,12 +30,14 @@ import ( type Ctx struct { app *App route *Route - next bool - error error + index int + method string + path string params *[]string values []string compress bool Fasthttp *fasthttp.RequestCtx + error error } // Range info of range header @@ -55,20 +57,23 @@ var poolCtx = sync.Pool{ } // Acquire Ctx from pool -func acquireCtx() *Ctx { +func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx { ctx := poolCtx.Get().(*Ctx) + ctx.index = -1 + ctx.Fasthttp = fctx return ctx } // Return Ctx to pool func releaseCtx(ctx *Ctx) { ctx.route = nil - ctx.next = false - ctx.error = nil + ctx.method = "" + ctx.path = "" ctx.params = nil ctx.values = nil ctx.compress = false ctx.Fasthttp = nil + ctx.error = nil poolCtx.Put(ctx) } @@ -522,12 +527,12 @@ func (ctx *Ctx) MultipartForm() (*multipart.Form, error) { // Next : https://fiber.wiki/context#next func (ctx *Ctx) Next(err ...error) { ctx.route = nil - ctx.next = true ctx.params = nil ctx.values = nil if len(err) > 0 { ctx.error = err[0] } + ctx.app.next(ctx) } // OriginalURL : https://fiber.wiki/context#originalurl diff --git a/group.go b/group.go index edc84c37d2..158cd2d1f4 100644 --- a/group.go +++ b/group.go @@ -31,7 +31,7 @@ func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group { // Clean path by removing double "//" => "/" prefix = strings.Replace(prefix, "//", "/", -1) if len(handlers) > 0 { - grp.app.registerMethod("USE", prefix, "", handlers...) + grp.app.registerMethod("USE", prefix, handlers...) } return &Group{ prefix: prefix, @@ -45,6 +45,18 @@ func (grp *Group) Static(args ...string) *Group { return grp } +func groupPaths(prefix, path string) string { + // `/v1`+`/` => `/v1`+`` + if path == "/" { + path = prefix + } else { + path = prefix + path + } + // Remove duplicate slashes `//` + path = strings.Replace(path, "//", "/", -1) + return path +} + // Use : https://fiber.wiki/application#http-methods func (grp *Group) Use(args ...interface{}) *Group { var path = "" @@ -59,67 +71,78 @@ func (grp *Group) Use(args ...interface{}) *Group { log.Fatalf("Invalid handlerrrr: %v", reflect.TypeOf(arg)) } } - grp.app.registerMethod("USE", grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod("USE", path, handlers...) return grp } // Connect : https://fiber.wiki/application#http-methods func (grp *Group) Connect(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodConnect, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodConnect, path, handlers...) return grp } // Put : https://fiber.wiki/application#http-methods func (grp *Group) Put(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodPut, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodPut, path, handlers...) return grp } // Post : https://fiber.wiki/application#http-methods func (grp *Group) Post(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodPost, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodPost, path, handlers...) return grp } // Delete : https://fiber.wiki/application#http-methods func (grp *Group) Delete(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodDelete, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodDelete, path, handlers...) return grp } // Head : https://fiber.wiki/application#http-methods func (grp *Group) Head(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodHead, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodHead, path, handlers...) return grp } // Patch : https://fiber.wiki/application#http-methods func (grp *Group) Patch(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodPatch, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodPatch, path, handlers...) return grp } // Options : https://fiber.wiki/application#http-methods func (grp *Group) Options(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodOptions, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodOptions, path, handlers...) return grp } // Trace : https://fiber.wiki/application#http-methods func (grp *Group) Trace(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodTrace, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodTrace, path, handlers...) return grp } // Get : https://fiber.wiki/application#http-methods func (grp *Group) Get(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod(http.MethodGet, grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod(http.MethodGet, path, handlers...) return grp } // All : https://fiber.wiki/application#http-methods func (grp *Group) All(path string, handlers ...func(*Ctx)) *Group { - grp.app.registerMethod("ALL", grp.prefix, path, handlers...) + path = groupPaths(grp.prefix, path) + grp.app.registerMethod("ALL", path, handlers...) return grp } diff --git a/router.go b/router.go index 2cd26692d0..abefd57b13 100644 --- a/router.go +++ b/router.go @@ -5,415 +5,374 @@ package fiber import ( - "fmt" "log" - "path/filepath" "regexp" "strings" - websocket "github.com/fasthttp/websocket" fasthttp "github.com/valyala/fasthttp" ) // Route struct type Route struct { - // HTTP method in uppercase, can be a * for "Use" & "All" routes - Method string - // Stores the original path - Path string - // Prefix is for ending wildcards or middleware - Prefix string - // Stores regex for complex paths - Regex *regexp.Regexp - // Stores params keys for :params / :optional? / * - Params []string - // Handler for context - HandlerCtx func(*Ctx) - // Handler for websockets - HandlerConn func(*Conn) -} + isMiddleware bool // is middleware route + isWebSocket bool // is websocket route -func (app *App) registerStatic(grpPrefix string, args ...string) { - var prefix = "/" - var root = "./" - // enable / disable gzipping somewhere? - // todo v2.0.0 - gzip := true + isStar bool // path == '*' + isSlash bool // path == '/' + isRegex bool // needs regex parsing - if len(args) == 1 { - root = args[0] - } - if len(args) == 2 { - prefix = args[0] - root = args[1] - } + Method string // http method + Path string // orginal path + Params []string // path params + Regexp *regexp.Regexp // regexp matcher + HandleCtx func(*Ctx) // ctx handler + HandleConn func(*Conn) // conn handler - // A non wildcard path must start with a '/' - if prefix != "*" && len(prefix) > 0 && prefix[0] != '/' { - prefix = "/" + prefix - } - // Prepend group prefix - if len(grpPrefix) > 0 { - // `/v1`+`/` => `/v1`+`` - if prefix == "/" { - prefix = grpPrefix - } else { - prefix = grpPrefix + prefix - } - // Remove duplicate slashes `//` - prefix = strings.Replace(prefix, "//", "/", -1) - } - // Empty or '/*' path equals "match anything" - // TODO fix * for paths with grpprefix - if prefix == "/*" { - prefix = "*" - } - // Lets get all files from root - files, _, err := getFiles(root) - if err != nil { - log.Fatal("Static: ", err) - } - // ./static/compiled => static/compiled - mount := filepath.Clean(root) - - if !app.Settings.CaseSensitive { - prefix = strings.ToLower(prefix) - } - if !app.Settings.StrictRouting && len(prefix) > 1 { - prefix = strings.TrimRight(prefix, "/") - } - - // Loop over all files - for _, file := range files { - // Ignore the .gzipped files by fasthttp - if strings.Contains(file, ".fasthttp.gz") { - continue - } - // Time to create a fake path for the route match - // static/index.html => /index.html - path := filepath.Join(prefix, strings.Replace(file, mount, "", 1)) - // for windows: static\index.html => /index.html - path = filepath.ToSlash(path) - // Store file path to use in ctx handler - filePath := file +} - if len(prefix) > 1 && strings.Contains(prefix, "*") { - app.routes = append(app.routes, &Route{ - Method: "GET", - Path: path, - Prefix: strings.Split(prefix, "*")[0], - HandlerCtx: func(c *Ctx) { - c.SendFile(filePath, gzip) - }, - }) +func (app *App) next(ctx *Ctx) { + for ; ctx.index < len(app.routes)-1; ctx.index++ { + ctx.index++ + route := app.routes[ctx.index] + if route.match(ctx.method, ctx.path) { + route.HandleCtx(ctx) return } - // If the file is an index.html, bind the prefix to index.html directly - if filepath.Base(filePath) == "index.html" || filepath.Base(filePath) == "index.htm" { - app.routes = append(app.routes, &Route{ - Method: "GET", - Path: prefix, - HandlerCtx: func(c *Ctx) { - c.SendFile(filePath, gzip) - }, - }) - } - if !app.Settings.CaseSensitive { - path = strings.ToLower(path) - } - if !app.Settings.StrictRouting && len(prefix) > 1 { - path = strings.TrimRight(path, "/") - } - // Add the route + SendFile(filepath) to routes - app.routes = append(app.routes, &Route{ - Method: "GET", - Path: path, - HandlerCtx: func(c *Ctx) { - c.SendFile(filePath, gzip) - }, - }) } } -func (app *App) registerWebSocket(method, group, path string, handler func(*Conn)) { - if len(path) > 0 && path[0] != '/' { - path = "/" + path - } - if len(group) > 0 { - // `/v1`+`/` => `/v1`+`` - if path == "/" { - path = group - } else { - path = group + path + +func (r *Route) match(method, path string) bool { + // is route middleware? matches all http methods + if r.isMiddleware { + // '*' or '/' means its a valid match + if r.isStar || r.isSlash { + return true } - // Remove duplicate slashes `//` - path = strings.Replace(path, "//", "/", -1) - } - // Routes are case insensitive by default - if !app.Settings.CaseSensitive { - path = strings.ToLower(path) - } - if !app.Settings.StrictRouting && len(path) > 1 { - path = strings.TrimRight(path, "/") + // if midware path starts with req.path + if strings.HasPrefix(r.Path, path) { + return true + } + // middlewares dont support regex so bye! + return false + } + // non-middleware route, http method must match! + // the wildcard method is for .All() method + if r.Method != method && r.Method[0] != '*' { + return false + } + // '*' means we match anything + if r.isStar { + return true + } + // simple '/' match, why not r.Path == path? + // because bool is faster and you avoid + // unnecessary comparison between long paths + if r.isSlash && path == "/" { + return true + } + // does this route need regex matching? + if r.isRegex { + // req.path match regex pattern + if r.Regexp.MatchString(path) { + return true + // Need to think about how to pass regex params to context + // if len(r.Params) > 0 { + // matches := route.Regex.FindAllStringSubmatch(path, -1) + // if len(matches) > 0 && len(matches[0]) > 1 { + // params := matches[0][1:len(matches[0])] + // } + // } + } + return false } - // Get ':param' & ':optional?' & '*' from path - params := getParams(path) - if len(params) > 0 { - log.Fatal("WebSocket routes do not support path parameters: `:param, :optional?, *`") + // last thing to do is to check for a simple path match + if r.Path == path { + return true } - app.routes = append(app.routes, &Route{ - Method: method, Path: path, HandlerConn: handler, - }) + // Nothing match + return false } -func (app *App) registerMethod(method, group, path string, handlers ...func(*Ctx)) { - // No special paths for websockets + +func (app *App) registerMethod(method, path string, handlers ...func(*Ctx)) { + // Route requires atleast one handler if len(handlers) == 0 { log.Fatalf("Missing handler in route") } - // Set variables - var prefix string - var middleware = method == "USE" - // A non wildcard path must start with a '/' - if path != "*" && len(path) > 0 && path[0] != '/' { - path = "/" + path - } - // Prepend group prefix - if len(group) > 0 { - // `/v1`+`/` => `/v1`+`` - if path == "/" { - path = group - } else { - path = group + path - } - // Remove duplicate slashes `//` - path = strings.Replace(path, "//", "/", -1) + // Cannot have an empty path + if path == "" { + path = "/" } - // Empty or '/*' path equals "match anything" - // TODO fix * for paths with grpprefix - if path == "/*" || (middleware && path == "") { - path = "*" + // Path always start with a '/' or '*' + if path[0] != '/' && path[0] != '*' { + path = "/" + path } - if method == "ALL" || middleware { + // Set route booleans + var isMiddleware = method == "USE" + // Middleware / All allows all HTTP methods + if isMiddleware || method == "ALL" { method = "*" } - // Routes are case insensitive by default - if !app.Settings.CaseSensitive { - path = strings.ToLower(path) - } - if !app.Settings.StrictRouting && len(path) > 1 { - path = strings.TrimRight(path, "/") - } - // If the route can match anything - if path == "*" { - for i := range handlers { - app.routes = append(app.routes, &Route{ - Method: method, Path: path, HandlerCtx: handlers[i], - }) - } - return - } - // Get ':param' & ':optional?' & '*' from path - params := getParams(path) - // Enable prefix for midware - if len(params) == 0 && middleware { - prefix = path - } - - // If path has no params (simple path) - if len(params) == 0 { - for i := range handlers { - app.routes = append(app.routes, &Route{ - Method: method, Path: path, Prefix: prefix, HandlerCtx: handlers[i], - }) + var isStar = path == "*" || path == "/*" + // Middleware containing only a `/` equals wildcard + if isMiddleware && path == "/" { + isStar = true + } + var isSlash = path == "/" + var isRegex = false + // Route properties + var Params = getParams(path) + var Regexp *regexp.Regexp + // Params requires regex pattern + if len(Params) > 0 { + regex, err := getRegex(path) + if err != nil { + log.Fatal("Router: Invalid path pattern: " + path) } - return - } - - // If path only contains 1 wildcard, we can create a prefix - // If its a middleware, we also create a prefix - if len(params) == 1 && params[0] == "*" { - prefix = strings.Split(path, "*")[0] - for i := range handlers { - app.routes = append(app.routes, &Route{ - Method: method, Path: path, Prefix: prefix, - Params: params, HandlerCtx: handlers[i], - }) - } - return - } - // We have an :param or :optional? and need to compile a regex struct - regex, err := getRegex(path) - if err != nil { - log.Fatal("Router: Invalid path pattern: " + path) + isRegex = true + Regexp = regex } - // Add route with regex for i := range handlers { app.routes = append(app.routes, &Route{ - Method: method, Path: path, Regex: regex, - Params: params, HandlerCtx: handlers[i], + isMiddleware: isMiddleware, + isStar: isStar, + isSlash: isSlash, + isRegex: isRegex, + Method: method, + Path: path, + Regexp: Regexp, + HandleCtx: handlers[i], }) } } func (app *App) handler(fctx *fasthttp.RequestCtx) { - // Use this boolean to perform 404 not found at the end - var match = false // get custom context from sync pool - ctx := acquireCtx() + ctx := acquireCtx(fctx) defer releaseCtx(ctx) + + ctx.method = ctx.Method() + ctx.path = ctx.Path() ctx.app = app - ctx.compress = app.Settings.Compression - ctx.Fasthttp = fctx - // get path and method - path := ctx.Path() - if !app.Settings.CaseSensitive { - path = strings.ToLower(path) - } - if !app.Settings.StrictRouting && len(path) > 1 { - path = strings.TrimRight(path, "/") - } - method := ctx.Method() - // enable recovery - if app.recover != nil { - defer func() { - if r := recover(); r != nil { - err, ok := r.(error) - if !ok { - err = fmt.Errorf("%v", r) - } - ctx.error = err - app.recover(ctx) - } - }() - } - // loop trough routes - for _, route := range app.routes { - // Skip route if method does not match - if route.Method != "*" && route.Method != method { - continue - } - // Set route pointer if user wants to call .Route() - ctx.route = route - // wilcard or exact same path - // TODO v2: enable or disable case insensitive match - if route.Path == "*" || route.Path == path { - // if * always set the path to the wildcard parameter - if route.Path == "*" { - ctx.params = &[]string{"*"} - ctx.values = []string{path} - } - // ctx.Fasthttp.Request.Header.ConnectionUpgrade() - // Websocket request - if route.HandlerConn != nil && websocket.FastHTTPIsWebSocketUpgrade(fctx) { - // Try to upgrade - err := socketUpgrade.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) { - conn := acquireConn(fconn) - defer releaseConn(conn) - route.HandlerConn(conn) - }) - // Upgrading failed - if err != nil { - log.Fatalf("Failed to upgrade websocket connection") - } - return - } - // No handler for HTTP nor websocket - if route.HandlerCtx == nil { - continue - } - // Match found, 404 not needed - match = true - route.HandlerCtx(ctx) - // if next is not set, leave loop and release ctx - if !ctx.next { - break - } else { - // reset match to false - match = false - } - // set next to false for next iteration - ctx.next = false - // continue to go to the next route - continue - } - if route.Prefix != "" && strings.HasPrefix(path, route.Prefix) { - ctx.route = route - if strings.Contains(route.Path, "*") { - ctx.params = &[]string{"*"} - // ctx.values = matches[0][1:len(matches[0])] - // parse query source - ctx.values = []string{strings.Replace(path, route.Prefix, "", 1)} - } - // No handler for HTTP nor websocket - if route.HandlerCtx == nil { - continue - } - // Match found, 404 not needed - match = true - route.HandlerCtx(ctx) - // if next is not set, leave loop and release ctx - if !ctx.next { - break - } else { - // reset match to false - match = false - } - // set next to false for next iteration - ctx.next = false - // continue to go to the next route - continue - } - // Skip route if regex does not exist - if route.Regex == nil { - continue - } + app.next(ctx) +} +func (app *App) registerStatic(grpPrefix string, args ...string) { + // var prefix = "/" + // var root = "./" + // // enable / disable gzipping somewhere? + // // todo v2.0.0 + // gzip := true - // Skip route if regex does not match - if !route.Regex.MatchString(path) { - continue - } + // if len(args) == 1 { + // root = args[0] + // } + // if len(args) == 2 { + // prefix = args[0] + // root = args[1] + // } - // If we have parameters, lets find the matches - if len(route.Params) > 0 { - matches := route.Regex.FindAllStringSubmatch(path, -1) - // If we have matches, add params and values to context - if len(matches) > 0 && len(matches[0]) > 1 { - ctx.params = &route.Params - ctx.values = matches[0][1:len(matches[0])] - } - } - // No handler for HTTP nor websocket - if route.HandlerCtx == nil { - continue - } - // Match found, 404 not needed - match = true - route.HandlerCtx(ctx) - // if next is not set, leave loop and release ctx - if !ctx.next { - break - } else { - // reset match to false - match = false - } - // set next to false for next iteration - ctx.next = false - } - // Do we need to compress? - if ctx.compress { - compressDefaultCompression(fctx) - // switch app.Settings.CompressionLevel { - // case 2: - // compressBestSpeed(fctx) - // case 3: - // compressBestCompression(fctx) - // case 4: - // compressHuffmanOnly(fctx) - // default: // 1 - // compressDefaultCompression(fctx) - // } - } - // No match, send default 404 Not Found - if !match { - ctx.SendStatus(404) - } + // // A non wildcard path must start with a '/' + // if prefix != "*" && len(prefix) > 0 && prefix[0] != '/' { + // prefix = "/" + prefix + // } + // // Prepend group prefix + // if len(grpPrefix) > 0 { + // // `/v1`+`/` => `/v1`+`` + // if prefix == "/" { + // prefix = grpPrefix + // } else { + // prefix = grpPrefix + prefix + // } + // // Remove duplicate slashes `//` + // prefix = strings.Replace(prefix, "//", "/", -1) + // } + // // Empty or '/*' path equals "match anything" + // // TODO fix * for paths with grpprefix + // if prefix == "/*" { + // prefix = "*" + // } + // // Lets get all files from root + // files, _, err := getFiles(root) + // if err != nil { + // log.Fatal("Static: ", err) + // } + // // ./static/compiled => static/compiled + // mount := filepath.Clean(root) + + // if !app.Settings.CaseSensitive { + // prefix = strings.ToLower(prefix) + // } + // if !app.Settings.StrictRouting && len(prefix) > 1 { + // prefix = strings.TrimRight(prefix, "/") + // } + + // // Loop over all files + // for _, file := range files { + // // Ignore the .gzipped files by fasthttp + // if strings.Contains(file, ".fasthttp.gz") { + // continue + // } + // // Time to create a fake path for the route match + // // static/index.html => /index.html + // path := filepath.Join(prefix, strings.Replace(file, mount, "", 1)) + // // for windows: static\index.html => /index.html + // path = filepath.ToSlash(path) + // // Store file path to use in ctx handler + // filePath := file + + // if len(prefix) > 1 && strings.Contains(prefix, "*") { + // app.routes = append(app.routes, &Route{ + // Method: "GET", + // Path: path, + // Prefix: strings.Split(prefix, "*")[0], + // HandlerCtx: func(c *Ctx) { + // c.SendFile(filePath, gzip) + // }, + // }) + // return + // } + // // If the file is an index.html, bind the prefix to index.html directly + // if filepath.Base(filePath) == "index.html" || filepath.Base(filePath) == "index.htm" { + // app.routes = append(app.routes, &Route{ + // Method: "GET", + // Path: prefix, + // HandlerCtx: func(c *Ctx) { + // c.SendFile(filePath, gzip) + // }, + // }) + // } + // if !app.Settings.CaseSensitive { + // path = strings.ToLower(path) + // } + // if !app.Settings.StrictRouting && len(prefix) > 1 { + // path = strings.TrimRight(path, "/") + // } + // // Add the route + SendFile(filepath) to routes + // app.routes = append(app.routes, &Route{ + // Method: "GET", + // Path: path, + // HandlerCtx: func(c *Ctx) { + // c.SendFile(filePath, gzip) + // }, + // }) + // } } +func (app *App) registerWebSocket(method, group, path string, handle func(*Conn)) { + // if len(path) > 0 && path[0] != '/' { + // path = "/" + path + // } + // if len(group) > 0 { + // // `/v1`+`/` => `/v1`+`` + // if path == "/" { + // path = group + // } else { + // path = group + path + // } + // // Remove duplicate slashes `//` + // path = strings.Replace(path, "//", "/", -1) + // } + // // Routes are case insensitive by default + // if !app.Settings.CaseSensitive { + // path = strings.ToLower(path) + // } + // if !app.Settings.StrictRouting && len(path) > 1 { + // path = strings.TrimRight(path, "/") + // } + // // Get ':param' & ':optional?' & '*' from path + // params := getParams(path) + // if len(params) > 0 { + // log.Fatal("WebSocket routes do not support path parameters: `:param, :optional?, *`") + // } + // app.routes = append(app.routes, &Route{ + // Method: method, Path: path, HandlerConn: handler, + // }) +} + +// func (app *App) registerMethod(method, group, path string, handlers ...func(*Ctx)) { +// // No special paths for websockets +// if len(handlers) == 0 { +// log.Fatalf("Missing handler in route") +// } +// // Set variables +// var prefix string +// var middleware = method == "USE" +// // A non wildcard path must start with a '/' +// if path != "*" && len(path) > 0 && path[0] != '/' { +// path = "/" + path +// } +// // Prepend group prefix +// if len(group) > 0 { +// // `/v1`+`/` => `/v1`+`` +// if path == "/" { +// path = group +// } else { +// path = group + path +// } +// // Remove duplicate slashes `//` +// path = strings.Replace(path, "//", "/", -1) +// } +// // Empty or '/*' path equals "match anything" +// // TODO fix * for paths with grpprefix +// if path == "/*" || (middleware && path == "") { +// path = "*" +// } +// if method == "ALL" || middleware { +// method = "*" +// } +// // Routes are case insensitive by default +// if !app.Settings.CaseSensitive { +// path = strings.ToLower(path) +// } +// if !app.Settings.StrictRouting && len(path) > 1 { +// path = strings.TrimRight(path, "/") +// } +// // If the route can match anything +// if path == "*" { +// for i := range handlers { +// app.routes = append(app.routes, &Route{ +// Method: method, Path: path, HandlerCtx: handlers[i], +// }) +// } +// return +// } +// // Get ':param' & ':optional?' & '*' from path +// params := getParams(path) +// // Enable prefix for midware +// if len(params) == 0 && middleware { +// prefix = path +// } + +// // If path has no params (simple path) +// if len(params) == 0 { +// for i := range handlers { +// app.routes = append(app.routes, &Route{ +// Method: method, Path: path, Prefix: prefix, HandlerCtx: handlers[i], +// }) +// } +// return +// } + +// // If path only contains 1 wildcard, we can create a prefix +// // If its a middleware, we also create a prefix +// if len(params) == 1 && params[0] == "*" { +// prefix = strings.Split(path, "*")[0] +// for i := range handlers { +// app.routes = append(app.routes, &Route{ +// Method: method, Path: path, Prefix: prefix, +// Params: params, HandlerCtx: handlers[i], +// }) +// } +// return +// } +// // We have an :param or :optional? and need to compile a regex struct +// regex, err := getRegex(path) +// if err != nil { +// log.Fatal("Router: Invalid path pattern: " + path) +// } +// // Add route with regex +// for i := range handlers { +// app.routes = append(app.routes, &Route{ +// Method: method, Path: path, Regex: regex, +// Params: params, HandlerCtx: handlers[i], +// }) +// } +// } From 21590dffe9555071c1ef83959dda7275aef8edcc Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Wed, 4 Mar 2020 12:30:29 +0100 Subject: [PATCH 2/7] v1.8.2 --- app.go | 146 +++++------- app_test.go | 103 +++++---- context.go | 71 +++--- group.go | 38 +--- middleware/helmet.go | 4 +- middleware/logger.go | 13 +- middleware/logger.go.fasthttp.gz | Bin 0 -> 1333 bytes middleware/recover.go | 33 +++ router.go | 371 +++++++++++++++++-------------- utils.go | 27 +-- 10 files changed, 416 insertions(+), 390 deletions(-) create mode 100644 middleware/logger.go.fasthttp.gz create mode 100644 middleware/recover.go diff --git a/app.go b/app.go index 739e044d20..92d096a0c3 100644 --- a/app.go +++ b/app.go @@ -6,6 +6,7 @@ package fiber import ( "bufio" + "crypto/tls" "flag" "fmt" "io/ioutil" @@ -25,26 +26,26 @@ import ( ) // Version of Fiber -const Version = "1.8.0" +const Version = "1.8.2" type ( // App denotes the Fiber application. App struct { - server *fasthttp.Server - routes []*Route - child bool - recover func(*Ctx) - Settings *Settings + server *fasthttp.Server // Fasthttp server settings + routes []*Route // Route stack + child bool // If current process is a child ( for prefork ) + recover func(*Ctx) // Deprecated, use middleware.Recover + Settings *Settings // Fiber settings } // Map defines a generic map of type `map[string]interface{}`. Map map[string]interface{} // Settings is a struct holding the server settings Settings struct { - // fiber settings + // This will spawn multiple Go processes listening on the same port Prefork bool `default:"false"` - // Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different. Otherwise, the router treats "/foo" and "/foo/" as the same. + // Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different. StrictRouting bool `default:"false"` - // Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes. When disabled, "/Foo" and "/foo" are treated the same. + // Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes. CaseSensitive bool `default:"false"` // Enables the "Server: value" HTTP header. ServerHeader string `default:""` @@ -52,28 +53,13 @@ type ( Immutable bool `default:"false"` // Enables GZip / Deflate compression for all responses Compression bool `default:"false"` - // CompressionLevel int `default:"1"` - // fasthttp settings - // GETOnly bool `default:"false"` - // IdleTimeout time.Duration `default:"0"` - // Concurrency int `default:"256 * 1024"` - // ReadTimeout time.Duration `default:"0"` - // WriteTimeout time.Duration `default:"0"` - // TCPKeepalive bool `default:"false"` - // MaxConnsPerIP int `default:"0"` - // ReadBufferSize int `default:"4096"` - // WriteBufferSize int `default:"4096"` - // ConcurrencySleep time.Duration `default:"0"` - // DisableKeepAlive bool `default:"false"` - // ReduceMemoryUsage bool `default:"false"` - // MaxRequestsPerConn int `default:"0"` - // TCPKeepalivePeriod time.Duration `default:"0"` + // Max body size that the server accepts BodyLimit int `default:"4 * 1024 * 1024"` - // NoHeaderNormalizing bool `default:"false"` - // NoDefaultContentType bool `default:"false"` - // template settings - TemplateFolder string `default:""` - TemplateEngine string `default:""` + // Folder containing template files + TemplateFolder string `default:""` + // Template engine: html, amber, handlebars , mustache or pug + TemplateEngine string `default:""` + // Extension for the template files TemplateExtension string `default:""` } ) @@ -84,55 +70,39 @@ func init() { } // New : https://fiber.wiki/application#new -func New(settings ...*Settings) (app *App) { - var prefork bool - var child bool - for _, arg := range os.Args[1:] { - if arg == "-prefork" { +func New(settings ...*Settings) *App { + var prefork, child bool + // Loop trought args without using flag.Parse() + for i := range os.Args[1:] { + if os.Args[i] == "-prefork" { prefork = true - } else if arg == "-child" { + } else if os.Args[i] == "-child" { child = true } } - app = &App{ + // Create default app + app := &App{ child: child, + Settings: &Settings{ + Prefork: prefork, + BodyLimit: 4 * 1024 * 1024, + }, } + // If settings exist, set some defaults if len(settings) > 0 { - opt := settings[0] - if !opt.Prefork { - opt.Prefork = prefork + if settings[0].Prefork == false { // Default to -prefork flag if false + settings[0].Prefork = prefork } - if opt.Immutable { - getString = func(b []byte) string { - return string(b) - } + if settings[0].BodyLimit == 0 { // Default MaxRequestBodySize + settings[0].BodyLimit = 4 * 1024 * 1024 } - // if opt.Concurrency == 0 { - // opt.Concurrency = 256 * 1024 - // } - // if opt.ReadBufferSize == 0 { - // opt.ReadBufferSize = 4096 - // } - // if opt.WriteBufferSize == 0 { - // opt.WriteBufferSize = 4096 - // } - if opt.BodyLimit == 0 { - opt.BodyLimit = 4 * 1024 * 1024 + if settings[0].Immutable { // Replace unsafe conversion funcs + getString = func(b []byte) string { return string(b) } + getBytes = func(s string) []byte { return []byte(s) } } - // if opt.CompressionLevel == 0 { - // opt.CompressionLevel = 1 - // } - app.Settings = opt - return - } - app.Settings = &Settings{ - Prefork: prefork, - // Concurrency: 256 * 1024, - // ReadBufferSize: 4096, - // WriteBufferSize: 4096, - BodyLimit: 4 * 1024 * 1024, + app.Settings = settings[0] // Set custom settings } - return + return app } // Group : https://fiber.wiki/application#group @@ -147,8 +117,8 @@ func (app *App) Group(prefix string, handlers ...func(*Ctx)) *Group { } // Static : https://fiber.wiki/application#static -func (app *App) Static(args ...string) *App { - app.registerStatic("/", args...) +func (app *App) Static(prefix, root string) *App { + app.registerStatic(prefix, root) return app } @@ -231,18 +201,19 @@ func (app *App) All(path string, handlers ...func(*Ctx)) *App { } // WebSocket : https://fiber.wiki/application#websocket -func (app *App) WebSocket(path string, handler func(*Conn)) *App { - app.registerWebSocket(http.MethodGet, "", path, handler) +func (app *App) WebSocket(path string, handle func(*Conn)) *App { + app.registerWebSocket(http.MethodGet, path, handle) return app } // Recover : https://fiber.wiki/application#recover func (app *App) Recover(handler func(*Ctx)) { + log.Println("Warning: Recover(handler) is deprecated since v1.8.2, please use middleware.Recover(handler, error) instead.") app.recover = handler } // Listen : https://fiber.wiki/application#listen -func (app *App) Listen(address interface{}, tls ...string) error { +func (app *App) Listen(address interface{}, tlsconfig ...*tls.Config) error { addr, ok := address.(string) if !ok { port, ok := address.(int) @@ -273,9 +244,9 @@ func (app *App) Listen(address interface{}, tls ...string) error { } } - // enable TLS/HTTPS - if len(tls) > 1 { - return app.server.ServeTLS(ln, tls[0], tls[1]) + // TLS config + if len(tlsconfig) > 0 { + ln = tls.NewListener(ln, tlsconfig[0]) } return app.server.Serve(ln) } @@ -387,26 +358,11 @@ func (dl *disableLogger) Printf(format string, args ...interface{}) { func (app *App) newServer() *fasthttp.Server { return &fasthttp.Server{ - Handler: app.handler, - Name: app.Settings.ServerHeader, - // Concurrency: app.Settings.Concurrency, - // SleepWhenConcurrencyLimitsExceeded: app.Settings.ConcurrencySleep, - // DisableKeepalive: app.Settings.DisableKeepAlive, - // ReadBufferSize: app.Settings.ReadBufferSize, - // WriteBufferSize: app.Settings.WriteBufferSize, - // ReadTimeout: app.Settings.ReadTimeout, - // WriteTimeout: app.Settings.WriteTimeout, - // IdleTimeout: app.Settings.IdleTimeout, - // MaxConnsPerIP: app.Settings.MaxConnsPerIP, - // MaxRequestsPerConn: app.Settings.MaxRequestsPerConn, - // TCPKeepalive: app.Settings.TCPKeepalive, - // TCPKeepalivePeriod: app.Settings.TCPKeepalivePeriod, - MaxRequestBodySize: app.Settings.BodyLimit, - // ReduceMemoryUsage: app.Settings.ReduceMemoryUsage, - // GetOnly: app.Settings.GETOnly, - // DisableHeaderNamesNormalizing: app.Settings.NoHeaderNormalizing, + Handler: app.handler, + Name: app.Settings.ServerHeader, + MaxRequestBodySize: app.Settings.BodyLimit, NoDefaultServerHeader: app.Settings.ServerHeader == "", - // NoDefaultContentType: app.Settings.NoDefaultContentType, + Logger: &disableLogger{}, LogAllErrors: false, ErrorHandler: func(ctx *fasthttp.RequestCtx, err error) { diff --git a/app_test.go b/app_test.go index 9b29b25105..a5744b70e7 100644 --- a/app_test.go +++ b/app_test.go @@ -66,58 +66,57 @@ func Test_Methods(t *testing.T) { } -// func Test_Static(t *testing.T) { -// app := New() -// grp := app.Group("/v1") -// grp.Static("/v2", ".travis.yml") -// app.Static("/yesyes*", ".github/FUNDING.yml") -// app.Static("./.github") -// app.Static("/john", "./.github") -// req, _ := http.NewRequest("GET", "/stale.yml", nil) -// resp, err := app.Test(req) -// if err != nil { -// t.Fatalf(`%s: %s`, t.Name(), err) -// } -// if resp.StatusCode != 200 { -// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) -// } -// if resp.Header.Get("Content-Length") == "" { -// t.Fatalf(`%s: Missing Content-Length`, t.Name()) -// } -// req, _ = http.NewRequest("GET", "/yesyes/john/doe", nil) -// resp, err = app.Test(req) -// if err != nil { -// t.Fatalf(`%s: %s`, t.Name(), err) -// } -// if resp.StatusCode != 200 { -// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) -// } -// if resp.Header.Get("Content-Length") == "" { -// t.Fatalf(`%s: Missing Content-Length`, t.Name()) -// } -// req, _ = http.NewRequest("GET", "/john/stale.yml", nil) -// resp, err = app.Test(req) -// if err != nil { -// t.Fatalf(`%s: %s`, t.Name(), err) -// } -// if resp.StatusCode != 200 { -// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) -// } -// if resp.Header.Get("Content-Length") == "" { -// t.Fatalf(`%s: Missing Content-Length`, t.Name()) -// } -// req, _ = http.NewRequest("GET", "/v1/v2", nil) -// resp, err = app.Test(req) -// if err != nil { -// t.Fatalf(`%s: %s`, t.Name(), err) -// } -// if resp.StatusCode != 200 { -// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) -// } -// if resp.Header.Get("Content-Length") == "" { -// t.Fatalf(`%s: Missing Content-Length`, t.Name()) -// } -// } +func Test_Static(t *testing.T) { + app := New() + grp := app.Group("/v1") + grp.Static("/v2", ".travis.yml") + app.Static("/*", ".github/FUNDING.yml") + app.Static("/john", "./.github") + req, _ := http.NewRequest("GET", "/john/stale.yml", nil) + resp, err := app.Test(req) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + if resp.StatusCode != 200 { + t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) + } + if resp.Header.Get("Content-Length") == "" { + t.Fatalf(`%s: Missing Content-Length`, t.Name()) + } + req, _ = http.NewRequest("GET", "/yesyes/john/doe", nil) + resp, err = app.Test(req) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + if resp.StatusCode != 200 { + t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) + } + if resp.Header.Get("Content-Length") == "" { + t.Fatalf(`%s: Missing Content-Length`, t.Name()) + } + req, _ = http.NewRequest("GET", "/john/stale.yml", nil) + resp, err = app.Test(req) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + if resp.StatusCode != 200 { + t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) + } + if resp.Header.Get("Content-Length") == "" { + t.Fatalf(`%s: Missing Content-Length`, t.Name()) + } + req, _ = http.NewRequest("GET", "/v1/v2", nil) + resp, err = app.Test(req) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + if resp.StatusCode != 200 { + t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode) + } + if resp.Header.Get("Content-Length") == "" { + t.Fatalf(`%s: Missing Content-Length`, t.Name()) + } +} func Test_Group(t *testing.T) { app := New() diff --git a/context.go b/context.go index cb2975eb54..dd3e16b165 100644 --- a/context.go +++ b/context.go @@ -28,19 +28,19 @@ import ( // It has methods for the request query string, parameters, body, HTTP headers and so on. // For more information please visit our documentation: https://fiber.wiki/context type Ctx struct { - app *App - route *Route - index int - method string - path string - params *[]string - values []string - compress bool - Fasthttp *fasthttp.RequestCtx - error error -} - -// Range info of range header + app *App // Reference to *App + route *Route // Reference to *Route + index int // Index of the current stack + matched bool // If the context found a match in stack + method string // HTTP method + path string // HTTP path + values []string // Route parameter values + compress bool // If the response needs to be compressed + Fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx + err error // Contains error if catched +} + +// Range struct type Range struct { Type string Ranges []struct { @@ -49,6 +49,17 @@ type Range struct { } } +// Cookie struct +type Cookie struct { + Name string + Value string + Path string + Domain string + Expires time.Time + Secure bool + HTTPOnly bool +} + // Ctx pool var poolCtx = sync.Pool{ New: func() interface{} { @@ -60,6 +71,8 @@ var poolCtx = sync.Pool{ func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx { ctx := poolCtx.Get().(*Ctx) ctx.index = -1 + ctx.path = getString(fctx.URI().Path()) + ctx.method = getString(fctx.Request.Header.Method()) ctx.Fasthttp = fctx return ctx } @@ -67,13 +80,11 @@ func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx { // Return Ctx to pool func releaseCtx(ctx *Ctx) { ctx.route = nil - ctx.method = "" - ctx.path = "" - ctx.params = nil ctx.values = nil ctx.compress = false + ctx.matched = false ctx.Fasthttp = nil - ctx.error = nil + ctx.err = nil poolCtx.Put(ctx) } @@ -102,17 +113,6 @@ func releaseConn(conn *Conn) { poolConn.Put(conn) } -// Cookie : struct -type Cookie struct { - Name string - Value string - Path string - Domain string - Expires time.Time - Secure bool - HTTPOnly bool -} - // Accepts : https://fiber.wiki/context#accepts func (ctx *Ctx) Accepts(offers ...string) (offer string) { if len(offers) == 0 { @@ -363,7 +363,7 @@ func (ctx *Ctx) Download(file string, name ...string) { // Error returns err that is passed via Next(err) func (ctx *Ctx) Error() error { - return ctx.error + return ctx.err } // Format : https://fiber.wiki/context#format @@ -527,12 +527,11 @@ func (ctx *Ctx) MultipartForm() (*multipart.Form, error) { // Next : https://fiber.wiki/context#next func (ctx *Ctx) Next(err ...error) { ctx.route = nil - ctx.params = nil ctx.values = nil if len(err) > 0 { - ctx.error = err[0] + ctx.err = err[0] } - ctx.app.next(ctx) + ctx.app.nextRoute(ctx) } // OriginalURL : https://fiber.wiki/context#originalurl @@ -542,11 +541,11 @@ func (ctx *Ctx) OriginalURL() string { // Params : https://fiber.wiki/context#params func (ctx *Ctx) Params(key string) (value string) { - if ctx.params == nil { + if ctx.route.Params == nil { return } - for i := 0; i < len(*ctx.params); i++ { - if (*ctx.params)[i] == key { + for i := 0; i < len(ctx.route.Params); i++ { + if (ctx.route.Params)[i] == key { return ctx.values[i] } } @@ -730,7 +729,7 @@ func (ctx *Ctx) SendStatus(status int) { ctx.Fasthttp.Response.SetStatusCode(status) // Only set status body when there is no response body if len(ctx.Fasthttp.Response.Body()) == 0 { - ctx.Fasthttp.Response.SetBodyString(getStatus(status)) + ctx.Fasthttp.Response.SetBodyString(statusMessages[status]) } } diff --git a/group.go b/group.go index 158cd2d1f4..c41ccdc3cd 100644 --- a/group.go +++ b/group.go @@ -8,7 +8,6 @@ import ( "log" "net/http" "reflect" - "strings" ) // Group ... @@ -19,17 +18,7 @@ type Group struct { // Group : https://fiber.wiki/application#group func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group { - if len(prefix) > 0 && prefix[0] != '/' && prefix[0] != '*' { - prefix = "/" + prefix - } - // When grouping, always remove single slash - if len(grp.prefix) > 0 && prefix == "/" { - prefix = "" - } - // Prepent group prefix if exist - prefix = grp.prefix + prefix - // Clean path by removing double "//" => "/" - prefix = strings.Replace(prefix, "//", "/", -1) + prefix = groupPaths(grp.prefix, prefix) if len(handlers) > 0 { grp.app.registerMethod("USE", prefix, handlers...) } @@ -40,23 +29,12 @@ func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group { } // Static : https://fiber.wiki/application#static -func (grp *Group) Static(args ...string) *Group { - grp.app.registerStatic(grp.prefix, args...) +func (grp *Group) Static(prefix, root string) *Group { + prefix = groupPaths(grp.prefix, prefix) + grp.app.registerStatic(prefix, root) return grp } -func groupPaths(prefix, path string) string { - // `/v1`+`/` => `/v1`+`` - if path == "/" { - path = prefix - } else { - path = prefix + path - } - // Remove duplicate slashes `//` - path = strings.Replace(path, "//", "/", -1) - return path -} - // Use : https://fiber.wiki/application#http-methods func (grp *Group) Use(args ...interface{}) *Group { var path = "" @@ -68,7 +46,7 @@ func (grp *Group) Use(args ...interface{}) *Group { case func(*Ctx): handlers = append(handlers, arg) default: - log.Fatalf("Invalid handlerrrr: %v", reflect.TypeOf(arg)) + log.Fatalf("Invalid handler: %v", reflect.TypeOf(arg)) } } path = groupPaths(grp.prefix, path) @@ -147,12 +125,14 @@ func (grp *Group) All(path string, handlers ...func(*Ctx)) *Group { } // WebSocket : https://fiber.wiki/application#websocket -func (grp *Group) WebSocket(path string, handler func(*Conn)) *Group { - grp.app.registerWebSocket(http.MethodGet, grp.prefix, path, handler) +func (grp *Group) WebSocket(path string, handle func(*Conn)) *Group { + path = groupPaths(grp.prefix, path) + grp.app.registerWebSocket(http.MethodGet, path, handle) return grp } // Recover : https://fiber.wiki/application#recover func (grp *Group) Recover(handler func(*Ctx)) { + log.Println("Warning: Recover(handler) is deprecated since v1.8.2, please use middleware.Recover(handler, error) instead.") grp.app.recover = handler } diff --git a/middleware/helmet.go b/middleware/helmet.go index d92d1b89ce..ecb8ce5ea6 100644 --- a/middleware/helmet.go +++ b/middleware/helmet.go @@ -49,8 +49,8 @@ var HelmetConfigDefault = HelmetConfig{ XFrameOptions: "SAMEORIGIN", } -// Secure ... -func Secure(config ...HelmetConfig) func(*fiber.Ctx) { +// Helmet ... +func Helmet(config ...HelmetConfig) func(*fiber.Ctx) { // Init config var cfg HelmetConfig if len(config) > 0 { diff --git a/middleware/logger.go b/middleware/logger.go index 4f2aca7099..2c09ff071d 100644 --- a/middleware/logger.go +++ b/middleware/logger.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "strconv" "strings" "sync" "time" @@ -34,7 +35,7 @@ type LoggerConfig struct { // LoggerConfigDefault is the defaul Logger middleware config. var LoggerConfigDefault = LoggerConfig{ Skip: nil, - Format: "${time} - ${ip} - ${method} ${path}\t${ua}\n", + Format: "${time} ${method} ${path} - ${ip} - ${status} - ${latency}ms\n", TimeFormat: "15:04:05", Output: os.Stderr, } @@ -81,6 +82,11 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) { c.Next() return } + start := time.Now() + // handle request + c.Next() + // build log + stop := time.Now() buf := pool.Get().(*bytes.Buffer) buf.Reset() defer pool.Put(buf) @@ -104,6 +110,10 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) { return buf.WriteString(c.OriginalURL()) case "ua": return buf.WriteString(c.Get(fiber.HeaderUserAgent)) + case "latency": + return buf.WriteString(stop.Sub(start).String()) + case "status": + return buf.WriteString(strconv.Itoa(c.Fasthttp.Response.StatusCode())) default: switch { case strings.HasPrefix(tag, "header:"): @@ -124,6 +134,5 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) { if _, err := cfg.Output.Write(buf.Bytes()); err != nil { fmt.Println(err) } - c.Next() } } diff --git a/middleware/logger.go.fasthttp.gz b/middleware/logger.go.fasthttp.gz new file mode 100644 index 0000000000000000000000000000000000000000..8767ca83f8430c7346abcce55ad163b1663ce8e0 GIT binary patch literal 1333 zcmV-51A`DJdr_!~K)VN6#DRHiG(|&NG zYL6SZm4T(ZgU_cE|GgS^xwp7E*OCvIo#)wk+nH@eek)3ZTF&PcE``Rx4}4j-N)tqZ z?}f7!p$Yk|sA+ggH9VA+8q%^U-TSJ^hZmAHhRo!JrKCkSOLJ9cr7GkMbvFEl2a9*2 zT8T}S20pD?+!q_tT~4qLd@sx3`c}3u$3iw} zKtRzoIZ4$3DKNaWbvdg3dP(;&%#`BAEQ8ld z*Mc^C(E_1TWht8y3cIwFv~cTlxD#56S%v05w+&C$9Kjw=;bbja`>c@`YTm=ix)rqO zKhw#&6aD9g1DRzoRmR9!g>WaT4$TCZApzMY(CI3Hg))?YB}srO5@@v|m5**{EKs9P zpc4r!P|Q(Je!j)kiv<4ZP_M=h_KE+;?_8-{iS{e-Jr@mI7nb=CS)&s#h*~qrvR127 zi@KIgnU0A(=;=QQb3eyGE&f*6`_w0Of_QT}tJn+4p zU3ZQ7&{5kFNG^({mPDvwxj;Q6&4AG8#EsS8P%D#O(;T%9d~YOC>0eqB>cH7~OsXxdE?KJT3gnp3VVo$nuH8}zM*T^c_1l|h&Uj-$5(V9RwMlnn_zFOW* z43a~S?V;pT;sjB?IjpcM=>949MwP;T*G&Aj`P6x zWC0a6Zr>PQz}e6XJf^vDkejnFf$#NSG0k11?dh>(c~PdWfG#c|3^!~W>7I2iS#Hto z;%Ihi@qlV~0ClH>j!vw-Uj04|ml^PIF%&N)LsZ!No&lN5wHr+AEhENwG$8~Rw^)yna8yE%* z)4%MB0(Df2Ta0eLusDJ5&!5MCwY|w_<&A0SYH6&zX4uimaeQ1l=E7j=k@aYvw zySE*?A7{3Mq}LT<8$E-k7Ii(?Y!x^hULVB|=5w5Pfj-bW;U&i>?y%D9{JN3y#Mw literal 0 HcmV?d00001 diff --git a/middleware/recover.go b/middleware/recover.go new file mode 100644 index 0000000000..dc7c204126 --- /dev/null +++ b/middleware/recover.go @@ -0,0 +1,33 @@ +package middleware + +import ( + "fmt" + "log" + + "github.com/gofiber/fiber" +) + +// Recover ... +func Recover(handle ...func(*fiber.Ctx, error)) func(*fiber.Ctx) { + h := func(c *fiber.Ctx, err error) { + log.Println(err) + c.SendStatus(500) + } + // Init custom error handler if exist + if len(handle) > 0 { + h = handle[0] + } + // Return middleware handle + return func(c *fiber.Ctx) { + defer func() { + if r := recover(); r != nil { + err, ok := r.(error) + if !ok { + err = fmt.Errorf("%v", r) + } + h(c, err) + } + }() + c.Next() + } +} diff --git a/router.go b/router.go index abefd57b13..01e52293a3 100644 --- a/router.go +++ b/router.go @@ -6,9 +6,12 @@ package fiber import ( "log" + "os" + "path/filepath" "regexp" "strings" + websocket "github.com/fasthttp/websocket" fasthttp "github.com/valyala/fasthttp" ) @@ -21,76 +24,126 @@ type Route struct { isSlash bool // path == '/' isRegex bool // needs regex parsing - Method string // http method - Path string // orginal path - Params []string // path params - Regexp *regexp.Regexp // regexp matcher - HandleCtx func(*Ctx) // ctx handler - HandleConn func(*Conn) // conn handler + Method string // http method + Path string // orginal path + Params []string // path params + Regexp *regexp.Regexp // regexp matcher + + HandleCtx func(*Ctx) // ctx handler + HandleConn func(*Conn) // conn handler } -func (app *App) next(ctx *Ctx) { - for ; ctx.index < len(app.routes)-1; ctx.index++ { +func (app *App) nextRoute(ctx *Ctx) { + lenr := len(app.routes) - 1 + for ctx.index < lenr { ctx.index++ route := app.routes[ctx.index] - if route.match(ctx.method, ctx.path) { - route.HandleCtx(ctx) + match, values := route.matchRoute(ctx.method, ctx.path) + if match { + ctx.route = route + if !ctx.matched { + ctx.matched = true + } + if len(values) > 0 { + ctx.values = values + } + if route.isWebSocket { + if err := websocketUpgrader.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) { + conn := acquireConn(fconn) + defer releaseConn(conn) + route.HandleConn(conn) + }); err != nil { // Upgrading failed + ctx.SendStatus(400) + } + } else { + route.HandleCtx(ctx) + } return } } + if !ctx.matched { + ctx.SendStatus(404) + } } -func (r *Route) match(method, path string) bool { +func (r *Route) matchRoute(method, path string) (match bool, values []string) { // is route middleware? matches all http methods if r.isMiddleware { // '*' or '/' means its a valid match if r.isStar || r.isSlash { - return true + return true, nil } // if midware path starts with req.path - if strings.HasPrefix(r.Path, path) { - return true + if strings.HasPrefix(path, r.Path) { + return true, nil } // middlewares dont support regex so bye! - return false + return false, nil } // non-middleware route, http method must match! // the wildcard method is for .All() method if r.Method != method && r.Method[0] != '*' { - return false + return false, nil } // '*' means we match anything if r.isStar { - return true + return true, nil } - // simple '/' match, why not r.Path == path? - // because bool is faster and you avoid - // unnecessary comparison between long paths + // simple '/' bool, so you avoid unnecessary comparison for long paths if r.isSlash && path == "/" { - return true + return true, nil } // does this route need regex matching? if r.isRegex { // req.path match regex pattern if r.Regexp.MatchString(path) { - return true - // Need to think about how to pass regex params to context - // if len(r.Params) > 0 { - // matches := route.Regex.FindAllStringSubmatch(path, -1) - // if len(matches) > 0 && len(matches[0]) > 1 { - // params := matches[0][1:len(matches[0])] - // } - // } + // do we have parameters + if len(r.Params) > 0 { + // get values for parameters + matches := r.Regexp.FindAllStringSubmatch(path, -1) + // did we get the values? + if len(matches) > 0 && len(matches[0]) > 1 { + values = matches[0][1:len(matches[0])] + return true, values + } + return false, nil + } + return true, nil } - return false + return false, nil } // last thing to do is to check for a simple path match if r.Path == path { - return true + return true, nil } // Nothing match - return false + return false, nil +} + +func (app *App) handler(fctx *fasthttp.RequestCtx) { + // get fiber context from sync pool + ctx := acquireCtx(fctx) + defer releaseCtx(ctx) + + // attach app poiner and compress settings + ctx.app = app + ctx.compress = app.Settings.Compression + + // Case sensitive routing + if !app.Settings.CaseSensitive { + ctx.path = strings.ToLower(ctx.path) + } + // Strict routing + if !app.Settings.StrictRouting && len(ctx.path) > 1 { + ctx.path = strings.TrimRight(ctx.path, "/") + } + + app.nextRoute(ctx) + + if ctx.compress { + compressResponse(fctx) + } } func (app *App) registerMethod(method, path string, handlers ...func(*Ctx)) { @@ -106,6 +159,16 @@ func (app *App) registerMethod(method, path string, handlers ...func(*Ctx)) { if path[0] != '/' && path[0] != '*' { path = "/" + path } + + // Case sensitive routing, all to lowercase + if !app.Settings.CaseSensitive { + path = strings.ToLower(path) + } + // Strict routing, remove last `/` + if !app.Settings.StrictRouting && len(path) > 1 { + path = strings.TrimRight(path, "/") + } + // Set route booleans var isMiddleware = method == "USE" // Middleware / All allows all HTTP methods @@ -139,23 +202,129 @@ func (app *App) registerMethod(method, path string, handlers ...func(*Ctx)) { isRegex: isRegex, Method: method, Path: path, + Params: Params, Regexp: Regexp, HandleCtx: handlers[i], }) } } -func (app *App) handler(fctx *fasthttp.RequestCtx) { - // get custom context from sync pool - ctx := acquireCtx(fctx) - defer releaseCtx(ctx) - ctx.method = ctx.Method() - ctx.path = ctx.Path() - ctx.app = app +func (app *App) registerWebSocket(method, path string, handle func(*Conn)) { + // Cannot have an empty path + if path == "" { + path = "/" + } + // Path always start with a '/' or '*' + if path[0] != '/' && path[0] != '*' { + path = "/" + path + } - app.next(ctx) + // Case sensitive routing, all to lowercase + if !app.Settings.CaseSensitive { + path = strings.ToLower(path) + } + // Strict routing, remove last `/` + if !app.Settings.StrictRouting && len(path) > 1 { + path = strings.TrimRight(path, "/") + } + + var isWebSocket = true + + var isStar = path == "*" || path == "/*" + var isSlash = path == "/" + var isRegex = false + // Route properties + var Params = getParams(path) + var Regexp *regexp.Regexp + // Params requires regex pattern + if len(Params) > 0 { + regex, err := getRegex(path) + if err != nil { + log.Fatal("Router: Invalid path pattern: " + path) + } + isRegex = true + Regexp = regex + } + app.routes = append(app.routes, &Route{ + isWebSocket: isWebSocket, + isStar: isStar, + isSlash: isSlash, + isRegex: isRegex, + + Method: method, + Path: path, + Params: Params, + Regexp: Regexp, + HandleConn: handle, + }) } -func (app *App) registerStatic(grpPrefix string, args ...string) { + +func (app *App) registerStatic(prefix, root string) { + // Cannot have an empty prefix + if prefix == "" { + prefix = "/" + } + // prefix always start with a '/' or '*' + if prefix[0] != '/' && prefix[0] != '*' { + prefix = "/" + prefix + } + // Case sensitive routing, all to lowercase + if !app.Settings.CaseSensitive { + prefix = strings.ToLower(prefix) + } + + var isStar = prefix == "*" || prefix == "/*" + + files := map[string]string{} + // Clean root path + root = filepath.Clean(root) + // Check if root exist and is accessible + if _, err := os.Stat(root); err != nil { + log.Fatalf("%s", err) + } + // Store path url and file paths in map + if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + url := "*" + if !isStar { + // /css/style.css: static/css/style.css + url = filepath.Join(prefix, strings.Replace(path, root, "", 1)) + } + // \static\css: /static/css + url = filepath.ToSlash(url) + files[url] = path + if filepath.Base(path) == "index.html" { + files[url] = path + } + } + return err + }); err != nil { + log.Fatalf("%s", err) + } + compress := app.Settings.Compression + app.routes = append(app.routes, &Route{ + isMiddleware: true, + isStar: isStar, + Method: "*", + Path: prefix, + HandleCtx: func(c *Ctx) { + // Only allow GET & HEAD methods + if c.method == "GET" || c.method == "HEAD" { + path := "*" + if !isStar { + path = c.path + } + file := files[path] + if file != "" { + c.SendFile(file, compress) + return + } + } + c.matched = false + c.Next() + }, + }) + // var prefix = "/" // var root = "./" // // enable / disable gzipping somewhere? @@ -256,123 +425,3 @@ func (app *App) registerStatic(grpPrefix string, args ...string) { // }) // } } -func (app *App) registerWebSocket(method, group, path string, handle func(*Conn)) { - // if len(path) > 0 && path[0] != '/' { - // path = "/" + path - // } - // if len(group) > 0 { - // // `/v1`+`/` => `/v1`+`` - // if path == "/" { - // path = group - // } else { - // path = group + path - // } - // // Remove duplicate slashes `//` - // path = strings.Replace(path, "//", "/", -1) - // } - // // Routes are case insensitive by default - // if !app.Settings.CaseSensitive { - // path = strings.ToLower(path) - // } - // if !app.Settings.StrictRouting && len(path) > 1 { - // path = strings.TrimRight(path, "/") - // } - // // Get ':param' & ':optional?' & '*' from path - // params := getParams(path) - // if len(params) > 0 { - // log.Fatal("WebSocket routes do not support path parameters: `:param, :optional?, *`") - // } - // app.routes = append(app.routes, &Route{ - // Method: method, Path: path, HandlerConn: handler, - // }) -} - -// func (app *App) registerMethod(method, group, path string, handlers ...func(*Ctx)) { -// // No special paths for websockets -// if len(handlers) == 0 { -// log.Fatalf("Missing handler in route") -// } -// // Set variables -// var prefix string -// var middleware = method == "USE" -// // A non wildcard path must start with a '/' -// if path != "*" && len(path) > 0 && path[0] != '/' { -// path = "/" + path -// } -// // Prepend group prefix -// if len(group) > 0 { -// // `/v1`+`/` => `/v1`+`` -// if path == "/" { -// path = group -// } else { -// path = group + path -// } -// // Remove duplicate slashes `//` -// path = strings.Replace(path, "//", "/", -1) -// } -// // Empty or '/*' path equals "match anything" -// // TODO fix * for paths with grpprefix -// if path == "/*" || (middleware && path == "") { -// path = "*" -// } -// if method == "ALL" || middleware { -// method = "*" -// } -// // Routes are case insensitive by default -// if !app.Settings.CaseSensitive { -// path = strings.ToLower(path) -// } -// if !app.Settings.StrictRouting && len(path) > 1 { -// path = strings.TrimRight(path, "/") -// } -// // If the route can match anything -// if path == "*" { -// for i := range handlers { -// app.routes = append(app.routes, &Route{ -// Method: method, Path: path, HandlerCtx: handlers[i], -// }) -// } -// return -// } -// // Get ':param' & ':optional?' & '*' from path -// params := getParams(path) -// // Enable prefix for midware -// if len(params) == 0 && middleware { -// prefix = path -// } - -// // If path has no params (simple path) -// if len(params) == 0 { -// for i := range handlers { -// app.routes = append(app.routes, &Route{ -// Method: method, Path: path, Prefix: prefix, HandlerCtx: handlers[i], -// }) -// } -// return -// } - -// // If path only contains 1 wildcard, we can create a prefix -// // If its a middleware, we also create a prefix -// if len(params) == 1 && params[0] == "*" { -// prefix = strings.Split(path, "*")[0] -// for i := range handlers { -// app.routes = append(app.routes, &Route{ -// Method: method, Path: path, Prefix: prefix, -// Params: params, HandlerCtx: handlers[i], -// }) -// } -// return -// } -// // We have an :param or :optional? and need to compile a regex struct -// regex, err := getRegex(path) -// if err != nil { -// log.Fatal("Router: Invalid path pattern: " + path) -// } -// // Add route with regex -// for i := range handlers { -// app.routes = append(app.routes, &Route{ -// Method: method, Path: path, Regex: regex, -// Params: params, HandlerCtx: handlers[i], -// }) -// } -// } diff --git a/utils.go b/utils.go index ba980e06c5..dbf3e39a89 100644 --- a/utils.go +++ b/utils.go @@ -20,13 +20,9 @@ import ( fasthttp "github.com/valyala/fasthttp" ) -var compressDefaultCompression = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressDefaultCompression) - -// var compressBestSpeed = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressBestSpeed) -// var compressBestCompression = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressBestCompression) -// var compressHuffmanOnly = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressHuffmanOnly) var schemaDecoder = schema.NewDecoder() -var socketUpgrade = websocket.FastHTTPUpgrader{ +var compressResponse = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressDefaultCompression) +var websocketUpgrader = websocket.FastHTTPUpgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(fctx *fasthttp.RequestCtx) bool { @@ -34,6 +30,15 @@ var socketUpgrade = websocket.FastHTTPUpgrader{ }, } +func groupPaths(prefix, path string) string { + if path == "/" { + path = "" + } + path = prefix + path + path = strings.Replace(path, "//", "/", -1) + return path +} + func getParams(path string) (params []string) { if len(path) < 1 { return @@ -108,10 +113,6 @@ func getType(ext string) (mime string) { return mime } -func getStatus(status int) (msg string) { - return statusMessage[status] -} - // #nosec G103 // getString converts byte slice to a string without memory allocation. // See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ . @@ -122,7 +123,7 @@ var getString = func(b []byte) string { // #nosec G103 // getBytes converts string to a byte slice without memory allocation. // See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ . -func getBytes(s string) (b []byte) { +var getBytes = func(s string) (b []byte) { return *(*[]byte)(unsafe.Pointer(&s)) } @@ -162,8 +163,8 @@ const ( MIMEOctetStream = "application/octet-stream" ) -// HTTP status codes -var statusMessage = map[int]string{ +// HTTP status codes with messages +var statusMessages = map[int]string{ 100: "Continue", 101: "Switching Protocols", 102: "Processing", From 5e819e5ef508b8c029b326fceffb31eef53714ba Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Wed, 4 Mar 2020 12:41:38 +0100 Subject: [PATCH 3/7] Fix bool comparison --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 92d096a0c3..257cafc7c6 100644 --- a/app.go +++ b/app.go @@ -90,7 +90,7 @@ func New(settings ...*Settings) *App { } // If settings exist, set some defaults if len(settings) > 0 { - if settings[0].Prefork == false { // Default to -prefork flag if false + if !settings[0].Prefork { // Default to -prefork flag if false settings[0].Prefork = prefork } if settings[0].BodyLimit == 0 { // Default MaxRequestBodySize From 1f944d2a41e1cb18a607e4943adf718f92538cc5 Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Wed, 4 Mar 2020 12:43:59 +0100 Subject: [PATCH 4/7] Remove comments --- router.go | 100 ------------------------------------------------------ 1 file changed, 100 deletions(-) diff --git a/router.go b/router.go index 01e52293a3..aebf276758 100644 --- a/router.go +++ b/router.go @@ -324,104 +324,4 @@ func (app *App) registerStatic(prefix, root string) { c.Next() }, }) - - // var prefix = "/" - // var root = "./" - // // enable / disable gzipping somewhere? - // // todo v2.0.0 - // gzip := true - - // if len(args) == 1 { - // root = args[0] - // } - // if len(args) == 2 { - // prefix = args[0] - // root = args[1] - // } - - // // A non wildcard path must start with a '/' - // if prefix != "*" && len(prefix) > 0 && prefix[0] != '/' { - // prefix = "/" + prefix - // } - // // Prepend group prefix - // if len(grpPrefix) > 0 { - // // `/v1`+`/` => `/v1`+`` - // if prefix == "/" { - // prefix = grpPrefix - // } else { - // prefix = grpPrefix + prefix - // } - // // Remove duplicate slashes `//` - // prefix = strings.Replace(prefix, "//", "/", -1) - // } - // // Empty or '/*' path equals "match anything" - // // TODO fix * for paths with grpprefix - // if prefix == "/*" { - // prefix = "*" - // } - // // Lets get all files from root - // files, _, err := getFiles(root) - // if err != nil { - // log.Fatal("Static: ", err) - // } - // // ./static/compiled => static/compiled - // mount := filepath.Clean(root) - - // if !app.Settings.CaseSensitive { - // prefix = strings.ToLower(prefix) - // } - // if !app.Settings.StrictRouting && len(prefix) > 1 { - // prefix = strings.TrimRight(prefix, "/") - // } - - // // Loop over all files - // for _, file := range files { - // // Ignore the .gzipped files by fasthttp - // if strings.Contains(file, ".fasthttp.gz") { - // continue - // } - // // Time to create a fake path for the route match - // // static/index.html => /index.html - // path := filepath.Join(prefix, strings.Replace(file, mount, "", 1)) - // // for windows: static\index.html => /index.html - // path = filepath.ToSlash(path) - // // Store file path to use in ctx handler - // filePath := file - - // if len(prefix) > 1 && strings.Contains(prefix, "*") { - // app.routes = append(app.routes, &Route{ - // Method: "GET", - // Path: path, - // Prefix: strings.Split(prefix, "*")[0], - // HandlerCtx: func(c *Ctx) { - // c.SendFile(filePath, gzip) - // }, - // }) - // return - // } - // // If the file is an index.html, bind the prefix to index.html directly - // if filepath.Base(filePath) == "index.html" || filepath.Base(filePath) == "index.htm" { - // app.routes = append(app.routes, &Route{ - // Method: "GET", - // Path: prefix, - // HandlerCtx: func(c *Ctx) { - // c.SendFile(filePath, gzip) - // }, - // }) - // } - // if !app.Settings.CaseSensitive { - // path = strings.ToLower(path) - // } - // if !app.Settings.StrictRouting && len(prefix) > 1 { - // path = strings.TrimRight(path, "/") - // } - // // Add the route + SendFile(filepath) to routes - // app.routes = append(app.routes, &Route{ - // Method: "GET", - // Path: path, - // HandlerCtx: func(c *Ctx) { - // c.SendFile(filePath, gzip) - // }, - // }) - // } } From ef8ff905d645a4a4b1732e2fd49ba3e3a30529ba Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Wed, 4 Mar 2020 13:55:12 +0100 Subject: [PATCH 5/7] Update examples --- .github/README.md | 71 +++++++----- .github/README_de.md | 234 ++++++++++++++++++++++++++++++++------- .github/README_es.md | 234 ++++++++++++++++++++++++++++++++------- .github/README_fr.md | 234 ++++++++++++++++++++++++++++++++------- .github/README_ja.md | 235 +++++++++++++++++++++++++++++++++------- .github/README_ko.md | 234 ++++++++++++++++++++++++++++++++------- .github/README_pt.md | 234 ++++++++++++++++++++++++++++++++------- .github/README_tr.md | 206 +++++++++++++++++++++++++++++++---- .github/README_zh-CN.md | 141 ++++++++++++++++++------ 9 files changed, 1515 insertions(+), 308 deletions(-) diff --git a/.github/README.md b/.github/README.md index c2b5bdfc1e..00cc1c7c7e 100644 --- a/.github/README.md +++ b/.github/README.md @@ -153,12 +153,12 @@ func main() { ``` ### Serve static files - +https://fiber.wiki/application#static ```go func main() { app := fiber.New() - app.Static("/public") + app.Static("/", "/public") // => http://localhost:3000/js/script.js // => http://localhost:3000/css/style.css @@ -174,7 +174,8 @@ func main() { ``` ### Middleware & Next - +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -205,9 +206,10 @@ func main() { 📚 Show more code examples ### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render -Already supports: - +Supported engines: - [html](https://golang.org/pkg/html/template/) - [amber](https://github.com/eknkc/amber) - [handlebars](https://github.com/aymerick/raymond) @@ -241,7 +243,7 @@ func main() { ``` ### Grouping routes into chains - +https://fiber.wiki/application#group ```go func main() { app := fiber.New() @@ -263,8 +265,8 @@ func main() { } ``` -### Built-in logger - +### Middleware logger +https://fiber.wiki/middleware#logger ```go import ( "github.com/gofiber/fiber" @@ -288,6 +290,7 @@ func main() { ``` ### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. @@ -343,20 +346,27 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) @@ -364,14 +374,12 @@ func main() { ``` ### WebSocket support - +https://fiber.wiki/application#websocket ```go func main() { app := fiber.New() - app.WebSocket("/ws/:name", func(c *fiber.Conn) { - log.Println(c.Params("name")) - + app.WebSocket("/ws", func(c *fiber.Conn) { for { mt, msg, err := c.ReadMessage() if err != nil { @@ -389,26 +397,33 @@ func main() { } }) - // Listen on ws://localhost:3000/ws/john + // Listen on ws://localhost:3000/ws app.Listen(3000) } ``` -### Recover from `panic` - +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_de.md b/.github/README_de.md index bd5c6c7d3d..510aa4315d 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -122,27 +122,6 @@ Fiber ist **inspiriert** von Expressjs, dem beliebtesten Web-Framework im Intern Nachfolgend sind einige der gängigen Beispiele aufgeführt. Wenn du weitere Codebeispiele sehen möchten, besuche bitte unser ["Recipes Repository"](https://github.com/gofiber/recipes) oder besuche unsere [API Dokumentation](https://fiber.wiki). -### Serve static files - -```go -func main() { - app := fiber.New() - - app.Static("/public") - // => http://localhost:3000/js/script.js - // => http://localhost:3000/css/style.css - - app.Static("/prefix", "/public") - // => http://localhost:3000/prefix/js/script.js - // => http://localhost:3000/prefix/css/style.css - - app.Static("*", "/public/index.html") - // => http://localhost:3000/any/path/shows/index/html - - app.Listen(3000) -} -``` - ### Routing ```go @@ -171,8 +150,30 @@ func main() { } ``` -### Middleware & Next +### Serve static files +https://fiber.wiki/application#static +```go +func main() { + app := fiber.New() + app.Static("/", "/public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "/public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "/public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + app.Listen(3000) +} +``` + +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -200,7 +201,122 @@ func main() { ```
- 📜 Show more code examples + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` ### Custom 404 response @@ -209,9 +325,11 @@ func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -226,42 +344,84 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() -### Recover from panic + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` + +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_es.md b/.github/README_es.md index b693756d5a..84201d4451 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -122,27 +122,6 @@ Fiber está **inspirado** en Expressjs, el framework web más popular en Interne A continuación se enumeran algunos de los ejemplos comunes. Si desea ver más ejemplos de código, visite nuestro [repositorio de Recetas](https://github.com/gofiber/recipes) o nuestra [documentación de API](https://fiber.wiki) . -### Serve static files - -```go -func main() { - app := fiber.New() - - app.Static("/public") - // => http://localhost:3000/js/script.js - // => http://localhost:3000/css/style.css - - app.Static("/prefix", "/public") - // => http://localhost:3000/prefix/js/script.js - // => http://localhost:3000/prefix/css/style.css - - app.Static("*", "/public/index.html") - // => http://localhost:3000/any/path/shows/index/html - - app.Listen(3000) -} -``` - ### Routing ```go @@ -171,8 +150,30 @@ func main() { } ``` -### Middleware & Next +### Serve static files +https://fiber.wiki/application#static +```go +func main() { + app := fiber.New() + app.Static("/", "/public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "/public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "/public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + app.Listen(3000) +} +``` + +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -200,7 +201,122 @@ func main() { ```
- 📜 Show more code examples + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` ### Custom 404 response @@ -209,9 +325,11 @@ func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -226,42 +344,84 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() -### Recover from panic + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` + +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_fr.md b/.github/README_fr.md index 9659f5b338..ea9c972986 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -122,27 +122,6 @@ Fiber est **inspiré** par Express, le framework web le plus populaire d'Interne Ci-dessous quelques exemples courants. Si vous voulez voir plus d'exemples, rendez-vous sur notre ["Recipes repository"](https://github.com/gofiber/recipes) ou visitez notre [documentation API](https://fiber.wiki). -### Serve static files - -```go -func main() { - app := fiber.New() - - app.Static("/public") - // => http://localhost:3000/js/script.js - // => http://localhost:3000/css/style.css - - app.Static("/prefix", "/public") - // => http://localhost:3000/prefix/js/script.js - // => http://localhost:3000/prefix/css/style.css - - app.Static("*", "/public/index.html") - // => http://localhost:3000/any/path/shows/index/html - - app.Listen(3000) -} -``` - ### Routing ```go @@ -171,8 +150,30 @@ func main() { } ``` -### Middleware & Next +### Serve static files +https://fiber.wiki/application#static +```go +func main() { + app := fiber.New() + app.Static("/", "/public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "/public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "/public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + app.Listen(3000) +} +``` + +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -200,7 +201,122 @@ func main() { ```
- 📜 Voir plus d'exemples de code + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` ### Custom 404 response @@ -209,9 +325,11 @@ func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -226,42 +344,84 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() -### Recover from panic + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` + +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_ja.md b/.github/README_ja.md index 46edc0cb7b..85c32da8a1 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -126,27 +126,6 @@ Fiberは人気の高いWebフレームワークであるExpressjsに**インス 以下に一般的な例をいくつか示します。他のコード例をご覧になりたい場合は、 [Recipesリポジトリ](https://github.com/gofiber/recipes)または[APIドキュメント](https://fiber.wiki)にアクセスしてください。 -### Serve static files - -```go -func main() { - app := fiber.New() - - app.Static("/public") - // => http://localhost:3000/js/script.js - // => http://localhost:3000/css/style.css - - app.Static("/prefix", "/public") - // => http://localhost:3000/prefix/js/script.js - // => http://localhost:3000/prefix/css/style.css - - app.Static("*", "/public/index.html") - // => http://localhost:3000/any/path/shows/index/html - - app.Listen(3000) -} -``` - ### Routing ```go @@ -175,8 +154,30 @@ func main() { } ``` -### Middleware & Next +### Serve static files +https://fiber.wiki/application#static +```go +func main() { + app := fiber.New() + + app.Static("/", "/public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "/public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "/public/index.html") + // => http://localhost:3000/any/path/shows/index/html + app.Listen(3000) +} +``` + +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -204,7 +205,122 @@ func main() { ```
- 📜 Show more code examples + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` ### Custom 404 response @@ -213,9 +329,11 @@ func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -230,46 +348,89 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() + + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) -### Recover from panic + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ```
+ ## 💬 メディア - [ファイバーへようこそ— Go with❤️で記述されたExpress.jsスタイルのWebフレームワーク](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) *[ヴィック・ショースタク](https://github.com/koddr) 、2020年2月3日* diff --git a/.github/README_ko.md b/.github/README_ko.md index e0b595c5a9..10542413fd 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -122,27 +122,6 @@ Fiber는 인터넷에서 가장 인기있는 웹 프레임워크인 Express에 다음은 일반적인 예제들 입니다. 더 많은 코드 예제를 보고 싶다면, [Recipes 저장소](https://github.com/gofiber/recipes) 또는 [API 문서](https://fiber.wiki)를 방문하세요. -### Serve static files - -```go -func main() { - app := fiber.New() - - app.Static("/public") - // => http://localhost:3000/js/script.js - // => http://localhost:3000/css/style.css - - app.Static("/prefix", "/public") - // => http://localhost:3000/prefix/js/script.js - // => http://localhost:3000/prefix/css/style.css - - app.Static("*", "./public/index.html") - // => http://localhost:3000/anything/returns/the/index/file - - app.Listen(3000) -} -``` - ### Routing ```go @@ -171,8 +150,30 @@ func main() { } ``` -### Middleware & Next +### Serve static files +https://fiber.wiki/application#static +```go +func main() { + app := fiber.New() + app.Static("/", "/public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "/public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "/public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + app.Listen(3000) +} +``` + +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -200,7 +201,122 @@ func main() { ```
- 📜 Show more code examples + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` ### Custom 404 response @@ -209,9 +325,11 @@ func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -226,42 +344,84 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() + + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) -### Recover from panic + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_pt.md b/.github/README_pt.md index 1a1a79dbb9..e03944831e 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -122,27 +122,6 @@ O Fiber é **inspirado** no Express, o framework web mais popular da Internet. C Listados abaixo estão alguns exemplos comuns. Se você quiser ver mais exemplos de código, visite nosso [repositório de receitas](https://github.com/gofiber/recipes) ou a [documentação da API](https://fiber.wiki). -### Serve static files - -```go -func main() { - app := fiber.New() - - app.Static("/public") - // => http://localhost:3000/js/script.js - // => http://localhost:3000/css/style.css - - app.Static("/prefix", "/public") - // => http://localhost:3000/prefix/js/script.js - // => http://localhost:3000/prefix/css/style.css - - app.Static("*", "/public/index.html") - // => http://localhost:3000/any/path/shows/index/html - - app.Listen(3000) -} -``` - ### Routing ```go @@ -171,8 +150,30 @@ func main() { } ``` -### Middleware & Next +### Serve static files +https://fiber.wiki/application#static +```go +func main() { + app := fiber.New() + app.Static("/", "/public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "/public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "/public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + app.Listen(3000) +} +``` + +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -200,7 +201,122 @@ func main() { ```
- 📜 Show more code examples + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` ### Custom 404 response @@ -209,9 +325,11 @@ func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -226,42 +344,84 @@ func main() { ``` ### JSON Response - +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() -### Recover from panic + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` + +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_tr.md b/.github/README_tr.md index 2999607c26..0e0b88a04c 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -119,7 +119,7 @@ Fiber internet üzerinde en popüler olan Express web çatısından **esinlenmi Aşağıda yaygın örneklerden bazıları listelenmiştir. Daha fazla kod örneği görmek için, lütfen [Kod depomuzu](https://github.com/gofiber/recipes) veya [API dökümantasyonunu](https://fiber.wiki) ziyaret ediniz. -### Rotalar +### Routing ```go func main() { @@ -147,13 +147,13 @@ func main() { } ``` -### Statik dosya yönetimi - +### Serve static files +https://fiber.wiki/application#static ```go func main() { app := fiber.New() - app.Static("/public") + app.Static("/", "/public") // => http://localhost:3000/js/script.js // => http://localhost:3000/css/style.css @@ -168,8 +168,9 @@ func main() { } ``` -### Ara Katman & Sonraki - +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -197,18 +198,135 @@ func main() { ```
- 📚 Daha fazla kod örneği görüntüle + 📚 Show more code examples + +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render + +Supported engines: +- [html](https://golang.org/pkg/html/template/) +- [amber](https://github.com/eknkc/amber) +- [handlebars](https://github.com/aymerick/raymond) +- [mustache](https://github.com/cbroglie/mustache) +- [pug](https://github.com/Joker/jade) + +```go +func main() { + // You can setup template engine before initiation app: + app := fiber.New(&fiber.Settings{ + TemplateEngine: "mustache", + TemplateFolder: "./views", + TemplateExtension: ".tmpl", + }) + + // OR after initiation app at any convenient location: + app.Settings.TemplateEngine = "mustache" + app.Settings.TemplateFolder = "./views" + app.Settings.TemplateExtension = ".tmpl" + + // And now, you can call template `./views/home.tmpl` like this: + app.Get("/", func(c *fiber.Ctx) { + c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + // ... +} +``` + +### Grouping routes into chains +https://fiber.wiki/application#group +```go +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", cors()) // /api + + // API v1 routes + v1 := api.Group("/v1", mysql()) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", mongodb()) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} +``` + +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. -### Özel 404 Cevabı +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` + +### Custom 404 response ```go func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -222,43 +340,85 @@ func main() { } ``` -### JSON Cevabı - +### JSON Response +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` +### WebSocket support +https://fiber.wiki/application#websocket +```go +func main() { + app := fiber.New() -### Panikten Kurtarma + app.WebSocket("/ws", func(c *fiber.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + + log.Printf("recovery: %s", msg) + + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + }) + // Listen on ws://localhost:3000/ws + app.Listen(3000) +} +``` + +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 33292c094a..e44bbdd8d1 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -118,7 +118,7 @@ Fiber **受** Internet上最流行的Web框架Expressjs的**启发** 。我们 下面列出了一些常见示例。如果您想查看更多代码示例,请访问我们的[Recipes存储库](https://github.com/gofiber/recipes)或访问我们的[API文档](https://fiber.wiki) 。 -### 路由 +### Routing ```go func main() { @@ -146,13 +146,13 @@ func main() { } ``` -### 静态文件 - +### Serve static files +https://fiber.wiki/application#static ```go func main() { app := fiber.New() - app.Static("/public") + app.Static("/", "/public") // => http://localhost:3000/js/script.js // => http://localhost:3000/css/style.css @@ -167,8 +167,9 @@ func main() { } ``` -### 中间件 - +### Middleware & Next +https://fiber.wiki/routing#middleware +https://fiber.wiki/context#next ```go func main() { app := fiber.New() @@ -196,12 +197,13 @@ func main() { ```
- 📚 显示更多代码示例 + 📚 Show more code examples -### 模板引擎 - -已经支持的: +### Template engines +https://fiber.wiki/application#settings +https://fiber.wiki/context#render +Supported engines: - [html](https://golang.org/pkg/html/template/) - [amber](https://github.com/eknkc/amber) - [handlebars](https://github.com/aymerick/raymond) @@ -234,8 +236,8 @@ func main() { } ``` -### 组路由 - +### Grouping routes into chains +https://fiber.wiki/application#group ```go func main() { app := fiber.New() @@ -257,16 +259,73 @@ func main() { } ``` -### 自定义 404 响应 +### Middleware logger +https://fiber.wiki/middleware#logger +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // If you want to change default Logger config + loggerConfig := middleware.LoggerConfig{ + Format: "${time} - ${method} ${path}\n", + TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST", + } + + // Middleware for Logger with config + app.Use(middleware.Logger(loggerConfig)) + + // ... +} +``` + +### Cross-Origin Resource Sharing (CORS) +https://fiber.wiki/middleware#cors + +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. + +```go +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + +func main() { + app := fiber.New() + + // Connect CORS for each route as middleware + app.Use(middleware.CORS()) + + app.Get("/", func(c *fiber.Ctx) { + c.Send("CORS is enabled!") + }) + + app.Listen(3000) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` + +### Custom 404 response ```go func main() { app := fiber.New() app.Static("/public") + app.Get("/demo", func(c *fiber.Ctx) { c.Send("This is a demo!") }) + app.Post("/register", func(c *fiber.Ctx) { c.Send("Welcome!") }) @@ -280,36 +339,41 @@ func main() { } ``` -### JSON - +### JSON Response +https://fiber.wiki/context#json ```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + func main() { app := fiber.New() - type User struct { - Name string `json:"name"` - Age int `json:"age"` - } + app.Get("/user", func(c *fiber.Ctx) { + c.JSON(&User{"John", 20}) + // {"name":"John", "age":20} + }) - // Serialize JSON app.Get("/json", func(c *fiber.Ctx) { - c.JSON(&User{"John", 20}) - // => {"name":"John", "age":20} + c.JSON(&fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // {"success":true, "message":"Hi John!"} }) app.Listen(3000) } ``` -### WebSocket - +### WebSocket support +https://fiber.wiki/application#websocket ```go func main() { app := fiber.New() - app.WebSocket("/ws/:name", func(c *fiber.Conn) { - log.Println(c.Params("name")) - + app.WebSocket("/ws", func(c *fiber.Conn) { for { mt, msg, err := c.ReadMessage() if err != nil { @@ -327,26 +391,33 @@ func main() { } }) - // Listen on ws://localhost:3000/ws/john + // Listen on ws://localhost:3000/ws app.Listen(3000) } ``` -### 从 panic 中恢复 - +### Recover middleware +https://fiber.wiki/middleware#recover ```go +package main + +import ( + "github.com/gofiber/fiber" + "github.com/gofiber/fiber/middleware" +) + func main() { app := fiber.New() + app.Use(middleware.Recover(func(c *fiber.Ctx, err error) { + log.Println(err) // "Something went wrong!" + c.SendStatus(500) // Internal Server Error + }))) + app.Get("/", func(c *fiber.Ctx) { panic("Something went wrong!") }) - app.Recover(func(c *fiber.Ctx) { - c.Status(500).Send(c.Error()) - // => 500 "Something went wrong!" - }) - app.Listen(3000) } ``` From 5321ec650da1ad3538658c539fd01d7493e3bbfe Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Wed, 4 Mar 2020 13:57:49 +0100 Subject: [PATCH 6/7] Update go get example --- .github/README_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README_es.md b/.github/README_es.md index 84201d4451..2f8c737f75 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -88,7 +88,7 @@ En primer lugar, [descargue](https://golang.org/dl/) e instale Go. Se requiere ` La instalación se realiza con el comando [`go get`](https://golang.org/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) : ```bash -go get github.com/gofiber/fiber +go get github.com/gofiber/fiber/... ``` ## 🤖 Puntos de referencia From 7e2ee40624ffa12de0026fb289f0923f38b4f9d9 Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Wed, 4 Mar 2020 14:02:22 +0100 Subject: [PATCH 7/7] Update examples --- .github/README.md | 6 ++++++ .github/README_de.md | 8 +++++++- .github/README_es.md | 8 +++++++- .github/README_fr.md | 8 +++++++- .github/README_ja.md | 8 +++++++- .github/README_ko.md | 8 +++++++- .github/README_pt.md | 8 +++++++- .github/README_ru.md | 6 ++++++ .github/README_tr.md | 8 +++++++- .github/README_zh-CN.md | 8 +++++++- 10 files changed, 68 insertions(+), 8 deletions(-) diff --git a/.github/README.md b/.github/README.md index 00cc1c7c7e..8490282c12 100644 --- a/.github/README.md +++ b/.github/README.md @@ -452,6 +452,12 @@ If you want to say **thank you** and/or support the active development of `Fiber +
+ +
+ JustDave +
+

diff --git a/.github/README_de.md b/.github/README_de.md index 510aa4315d..92f7e4a71e 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -447,6 +447,12 @@ Falls du **danke** sagen möchtest und/oder aktiv die Entwicklung von `fiber` f
+
+ +
+ JustDave +
+

@@ -456,7 +462,7 @@ Falls du **danke** sagen möchtest und/oder aktiv die Entwicklung von `fiber` f

- koddr + Vic Shóstak
diff --git a/.github/README_es.md b/.github/README_es.md index 2f8c737f75..2cc74526f9 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -447,6 +447,12 @@ Si quiere **agradecer** y/o apoyar el desarrollo activo de la `Fiber`: +
+ +
+ JustDave +
+

@@ -456,7 +462,7 @@ Si quiere **agradecer** y/o apoyar el desarrollo activo de la `Fiber`:

- koddr + Vic Shóstak
diff --git a/.github/README_fr.md b/.github/README_fr.md index ea9c972986..8b9952229f 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -447,6 +447,12 @@ Si vous voulez nous remercier et/ou soutenir le développement actif de `Fiber`: +
+ +
+ JustDave +
+

@@ -456,7 +462,7 @@ Si vous voulez nous remercier et/ou soutenir le développement actif de `Fiber`:

- koddr + Vic Shóstak
diff --git a/.github/README_ja.md b/.github/README_ja.md index 85c32da8a1..2c3c6d0eb6 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -453,6 +453,12 @@ func main() { +
+ +
+ JustDave +
+

@@ -462,7 +468,7 @@ func main() {

- koddr + Vic Shóstak
diff --git a/.github/README_ko.md b/.github/README_ko.md index 10542413fd..70d55f219d 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -447,6 +447,12 @@ func main() { +
+ +
+ JustDave +
+

@@ -456,7 +462,7 @@ func main() {

- koddr + Vic Shóstak
diff --git a/.github/README_pt.md b/.github/README_pt.md index e03944831e..9027c34dd8 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -447,6 +447,12 @@ Se você quer **agradecer** e/ou apoiar o desenvolvimento ativo do `Fiber`: +
+ +
+ JustDave +
+

@@ -456,7 +462,7 @@ Se você quer **agradecer** e/ou apoiar o desenvolvimento ativo do `Fiber`:

- koddr + Vic Shóstak
diff --git a/.github/README_ru.md b/.github/README_ru.md index 32f460acaf..5f8a8e053a 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -438,6 +438,12 @@ func main() { +
+ +
+ JustDave +
+

diff --git a/.github/README_tr.md b/.github/README_tr.md index 0e0b88a04c..810269abc1 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -446,6 +446,12 @@ Eğer **teşekkür etmek** ve/veya `Fiber` ın aktif geliştirilmesini destekle +
+ +
+ JustDave +
+

@@ -455,7 +461,7 @@ Eğer **teşekkür etmek** ve/veya `Fiber` ın aktif geliştirilmesini destekle

- Vic Shóstak + Vic Shóstak
diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index e44bbdd8d1..6a20c30860 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -445,6 +445,12 @@ func main() { +
+ +
+ JustDave +
+

@@ -454,7 +460,7 @@ func main() {

- koddr + Vic Shóstak