diff --git a/.github/README.md b/.github/README.md index c2b5bdfc1e..8490282c12 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) } ``` @@ -437,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 bd5c6c7d3d..92f7e4a71e 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) } ``` @@ -287,6 +447,12 @@ Falls du **danke** sagen möchtest und/oder aktiv die Entwicklung von `fiber` f +
+ +
+ JustDave +
+

@@ -296,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 b693756d5a..2cc74526f9 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 @@ -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) } ``` @@ -287,6 +447,12 @@ Si quiere **agradecer** y/o apoyar el desarrollo activo de la `Fiber`: +
+ +
+ JustDave +
+

@@ -296,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 9659f5b338..8b9952229f 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) } ``` @@ -287,6 +447,12 @@ Si vous voulez nous remercier et/ou soutenir le développement actif de `Fiber`: +
+ +
+ JustDave +
+

@@ -296,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 46edc0cb7b..2c3c6d0eb6 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() -### 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) } ```
+ ## 💬 メディア - [ファイバーへようこそ— 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日* @@ -292,6 +453,12 @@ func main() { +
+ +
+ JustDave +
+

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

- koddr + Vic Shóstak
diff --git a/.github/README_ko.md b/.github/README_ko.md index e0b595c5a9..70d55f219d 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) } ``` @@ -287,6 +447,12 @@ func main() { +
+ +
+ JustDave +
+

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

- koddr + Vic Shóstak
diff --git a/.github/README_pt.md b/.github/README_pt.md index 1a1a79dbb9..9027c34dd8 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) } ``` @@ -287,6 +447,12 @@ Se você quer **agradecer** e/ou apoiar o desenvolvimento ativo do `Fiber`: +
+ +
+ JustDave +
+

@@ -296,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 2999607c26..810269abc1 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", + }) -### Özel 404 Cevabı + // 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 ```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) } ``` @@ -286,6 +446,12 @@ Eğer **teşekkür etmek** ve/veya `Fiber` ın aktif geliştirilmesini destekle +
+ +
+ JustDave +
+

@@ -295,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 33292c094a..6a20c30860 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) } ``` @@ -374,6 +445,12 @@ func main() { +
+ +
+ JustDave +
+

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

- koddr + Vic Shóstak
diff --git a/app.go b/app.go index 1f38994446..257cafc7c6 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,61 +70,45 @@ 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 { // 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 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, @@ -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 } @@ -166,83 +136,84 @@ 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 } // 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 43807f149f..a5744b70e7 100644 --- a/app_test.go +++ b/app_test.go @@ -70,10 +70,9 @@ 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("/*", ".github/FUNDING.yml") app.Static("/john", "./.github") - req, _ := http.NewRequest("GET", "/stale.yml", nil) + req, _ := http.NewRequest("GET", "/john/stale.yml", nil) resp, err := app.Test(req) if err != nil { t.Fatalf(`%s: %s`, t.Name(), err) diff --git a/context.go b/context.go index 676a69bfd9..dd3e16b165 100644 --- a/context.go +++ b/context.go @@ -28,17 +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 - next bool - error error - params *[]string - values []string - compress bool - Fasthttp *fasthttp.RequestCtx -} - -// 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 { @@ -47,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{} { @@ -55,20 +68,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.path = getString(fctx.URI().Path()) + ctx.method = getString(fctx.Request.Header.Method()) + ctx.Fasthttp = fctx return ctx } // Return Ctx to pool func releaseCtx(ctx *Ctx) { ctx.route = nil - ctx.next = false - ctx.error = nil - ctx.params = nil ctx.values = nil ctx.compress = false + ctx.matched = false ctx.Fasthttp = nil + ctx.err = nil poolCtx.Put(ctx) } @@ -97,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 { @@ -358,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 @@ -522,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.next = true - ctx.params = nil ctx.values = nil if len(err) > 0 { - ctx.error = err[0] + ctx.err = err[0] } + ctx.app.nextRoute(ctx) } // OriginalURL : https://fiber.wiki/context#originalurl @@ -537,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] } } @@ -725,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 edc84c37d2..c41ccdc3cd 100644 --- a/group.go +++ b/group.go @@ -8,7 +8,6 @@ import ( "log" "net/http" "reflect" - "strings" ) // Group ... @@ -19,19 +18,9 @@ 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...) + grp.app.registerMethod("USE", prefix, handlers...) } return &Group{ prefix: prefix, @@ -40,8 +29,9 @@ 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 } @@ -56,80 +46,93 @@ 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)) } } - 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 } // 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 0000000000..8767ca83f8 Binary files /dev/null and b/middleware/logger.go.fasthttp.gz differ 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 2cd26692d0..aebf276758 100644 --- a/router.go +++ b/router.go @@ -5,8 +5,8 @@ package fiber import ( - "fmt" "log" + "os" "path/filepath" "regexp" "strings" @@ -17,403 +17,311 @@ import ( // 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) -} - -func (app *App) registerStatic(grpPrefix string, args ...string) { - var prefix = "/" - var root = "./" - // enable / disable gzipping somewhere? - // todo v2.0.0 - gzip := true + isMiddleware bool // is middleware route + isWebSocket bool // is websocket route - if len(args) == 1 { - root = args[0] - } - if len(args) == 2 { - prefix = args[0] - root = args[1] - } + isStar bool // path == '*' + isSlash bool // path == '/' + isRegex bool // needs regex parsing - // 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) + Method string // http method + Path string // orginal path + Params []string // path params + Regexp *regexp.Regexp // regexp matcher - if !app.Settings.CaseSensitive { - prefix = strings.ToLower(prefix) - } - if !app.Settings.StrictRouting && len(prefix) > 1 { - prefix = strings.TrimRight(prefix, "/") - } + HandleCtx func(*Ctx) // ctx handler + HandleConn func(*Conn) // conn handler - // 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) nextRoute(ctx *Ctx) { + lenr := len(app.routes) - 1 + for ctx.index < lenr { + ctx.index++ + route := app.routes[ctx.index] + 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 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 !ctx.matched { + ctx.SendStatus(404) + } +} + +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, nil } - if !app.Settings.CaseSensitive { - path = strings.ToLower(path) + // if midware path starts with req.path + if strings.HasPrefix(path, r.Path) { + return true, nil } - if !app.Settings.StrictRouting && len(prefix) > 1 { - path = strings.TrimRight(path, "/") + // middlewares dont support regex so bye! + 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, nil + } + // '*' means we match anything + if r.isStar { + return true, nil + } + // simple '/' bool, so you avoid unnecessary comparison for long paths + if r.isSlash && path == "/" { + return true, nil + } + // does this route need regex matching? + if r.isRegex { + // req.path match regex pattern + if r.Regexp.MatchString(path) { + // 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 } - // 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 + return false, nil } - if len(group) > 0 { - // `/v1`+`/` => `/v1`+`` - if path == "/" { - path = group - } else { - path = group + path - } - // Remove duplicate slashes `//` - path = strings.Replace(path, "//", "/", -1) + // last thing to do is to check for a simple path match + if r.Path == path { + return true, nil } - // Routes are case insensitive by default + // Nothing match + 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 { - path = strings.ToLower(path) + ctx.path = strings.ToLower(ctx.path) } - if !app.Settings.StrictRouting && len(path) > 1 { - path = strings.TrimRight(path, "/") + // Strict routing + if !app.Settings.StrictRouting && len(ctx.path) > 1 { + ctx.path = strings.TrimRight(ctx.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.nextRoute(ctx) + + if ctx.compress { + compressResponse(fctx) } - 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 + +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) - } - // Empty or '/*' path equals "match anything" - // TODO fix * for paths with grpprefix - if path == "/*" || (middleware && path == "") { - path = "*" + // Cannot have an empty path + if path == "" { + path = "/" } - if method == "ALL" || middleware { - method = "*" + // Path always start with a '/' or '*' + if path[0] != '/' && path[0] != '*' { + path = "/" + path } - // Routes are case insensitive by default + + // 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, "/") } - // 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 + // Set route booleans + var isMiddleware = method == "USE" + // Middleware / All allows all HTTP methods + if isMiddleware || method == "ALL" { + method = "*" } - - // 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], - }) + 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 + isRegex = true + Regexp = regex } - // 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], + isMiddleware: isMiddleware, + isStar: isStar, + isSlash: isSlash, + isRegex: isRegex, + Method: method, + Path: path, + Params: Params, + 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() - defer releaseCtx(ctx) - ctx.app = app - ctx.compress = app.Settings.Compression - ctx.Fasthttp = fctx - // get path and method - path := ctx.Path() + +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 + } + + // 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, "/") } - 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 - } + var isWebSocket = true - // Skip route if regex does not match - if !route.Regex.MatchString(path) { - continue + 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, - // 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 + Method: method, + Path: path, + Params: Params, + Regexp: Regexp, + HandleConn: handle, + }) +} + +func (app *App) registerStatic(prefix, root string) { + // Cannot have an empty prefix + if prefix == "" { + prefix = "/" } - // 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) - // } + // prefix always start with a '/' or '*' + if prefix[0] != '/' && prefix[0] != '*' { + prefix = "/" + prefix } - // No match, send default 404 Not Found - if !match { - ctx.SendStatus(404) + // 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() + }, + }) } 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",