diff --git a/versioned_docs/version-v2.x/api/app.md b/versioned_docs/version-v2.x/api/app.md index cb38f7051fa..2e58a426d1a 100644 --- a/versioned_docs/version-v2.x/api/app.md +++ b/versioned_docs/version-v2.x/api/app.md @@ -127,7 +127,7 @@ func main() { micro := fiber.New() app.Mount("/john", micro) // GET /john/doe -> 200 OK - micro.Get("/doe", func(c *fiber.Ctx) error { + micro.Get("/doe", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) }) @@ -205,7 +205,7 @@ func main() { app.Route("/test", func(api fiber.Router) { api.Get("/foo", handler).Name("foo") // /test/foo (name: test.foo) - api.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar) + api.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar) }, "test.") log.Fatal(app.Listen(":3000")) @@ -261,7 +261,7 @@ func (app *App) Stack() [][]*Route ``` ```go title="Examples" -var handler = func(c *fiber.Ctx) error { return nil } +var handler = func(c fiber.Ctx) error { return nil } func main() { app := fiber.New() @@ -315,7 +315,7 @@ func (app *App) Name(name string) Router ``` ```go title="Examples" -var handler = func(c *fiber.Ctx) error { return nil } +var handler = func(c fiber.Ctx) error { return nil } func main() { app := fiber.New() @@ -417,7 +417,7 @@ func (app *App) GetRoute(name string) Route ``` ```go title="Examples" -var handler = func(c *fiber.Ctx) error { return nil } +var handler = func(c fiber.Ctx) error { return nil } func main() { app := fiber.New() @@ -454,7 +454,7 @@ When filterUseOption equal to true, it will filter the routes registered by the ```go title="Examples" func main() { app := fiber.New() - app.Post("/", func (c *fiber.Ctx) error { + app.Post("/", func (c fiber.Ctx) error { return c.SendString("Hello, World!") }).Name("index") data, _ := json.MarshalIndent(app.GetRoutes(true), "", " ") @@ -617,6 +617,16 @@ ln = tls.NewListener(ln, &tls.Config{Certificates: []tls.Certificate{cer}}) app.Listener(ln) ``` +## RegisterCustomConstraint + +RegisterCustomConstraint allows to register custom constraint. + +```go title="Signature" +func (app *App) RegisterCustomConstraint(constraint CustomConstraint) +``` + +See [Custom Constraint](../guide/routing.md#custom-constraint) section for more information. + ## Test Testing your application is done with the **Test** method. Use this method for creating `_test.go` files or when you need to debug your routing logic. The default timeout is `1s` if you want to disable a timeout altogether, pass `-1` as a second argument. @@ -627,7 +637,7 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (*http.Response, error ```go title="Examples" // Create route with GET method for test: -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { fmt.Println(c.BaseURL()) // => http://google.com fmt.Println(c.Get("X-Custom-Header")) // => hi @@ -654,4 +664,4 @@ Hooks is a method to return [hooks](../guide/hooks.md) property. ```go title="Signature" func (app *App) Hooks() *Hooks -``` \ No newline at end of file +``` diff --git a/versioned_docs/version-v2.x/api/client.md b/versioned_docs/version-v2.x/api/client.md index ffbe308f948..ae174916b73 100644 --- a/versioned_docs/version-v2.x/api/client.md +++ b/versioned_docs/version-v2.x/api/client.md @@ -22,7 +22,7 @@ func (c *Client) Delete(url string) *Agent Here we present a brief example demonstrating the simulation of a proxy using our `*fiber.Agent` methods. ```go // Get something -func getSomething(c *fiber.Ctx) (err error) { +func getSomething(c fiber.Ctx) (err error) { agent := fiber.Get("") statusCode, body, errs := agent.Bytes() if len(errs) > 0 { @@ -43,7 +43,7 @@ func getSomething(c *fiber.Ctx) (err error) { } // Post something -func createSomething(c *fiber.Ctx) (err error) { +func createSomething(c fiber.Ctx) (err error) { agent := fiber.Post("") agent.Body(c.Body()) // set body received by request statusCode, body, errs := agent.Bytes() @@ -271,7 +271,7 @@ agent.BodyStream(strings.NewReader("body=stream"), -1) JSON sends a JSON request by setting the Content-Type header to the `ctype` parameter. If no `ctype` is passed in, the header is set to `application/json`. ```go title="Signature" -func (a *Agent) JSON(v interface{}, ctype ...string) *Agent +func (a *Agent) JSON(v any, ctype ...string) *Agent ``` ```go title="Example" @@ -284,7 +284,7 @@ agent.JSON(fiber.Map{"success": true}) XML sends an XML request by setting the Content-Type header to `application/xml`. ```go title="Signature" -func (a *Agent) XML(v interface{}) *Agent +func (a *Agent) XML(v any) *Agent ``` ```go title="Example" @@ -636,7 +636,7 @@ code, body, errs := agent.String() Struct returns the status code, bytes body and errors of url. And bytes body will be unmarshalled to given v. ```go title="Signature" -func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) +func (a *Agent) Struct(v any) (code int, body []byte, errs []error) ``` ```go title="Example" diff --git a/versioned_docs/version-v2.x/api/ctx.md b/versioned_docs/version-v2.x/api/ctx.md index 48820a1de4d..1b0661756c3 100644 --- a/versioned_docs/version-v2.x/api/ctx.md +++ b/versioned_docs/version-v2.x/api/ctx.md @@ -26,7 +26,7 @@ func (c *Ctx) AcceptsLanguages(offers ...string) string ```go title="Example" // Accept: text/html, application/json; q=0.8, text/plain; q=0.5; charset="utf-8" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Accepts("html") // "html" c.Accepts("text/html") // "text/html" c.Accepts("json", "text") // "json" @@ -41,7 +41,7 @@ app.Get("/", func(c *fiber.Ctx) error { ```go title="Example 2" // Accept: text/html, text/*, application/json, */*; q=0 -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Accepts("text/plain", "application/json") // "application/json", due to specificity c.Accepts("application/json", "text/html") // "text/html", due to first match c.Accepts("image/png") // "", due to */* without q factor 0 is Not Acceptable @@ -54,7 +54,7 @@ Media-Type parameters are supported. ```go title="Example 3" // Accept: text/plain, application/json; version=1; foo=bar -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Extra parameters in the accept are ignored c.Accepts("text/plain;format=flowed") // "text/plain;format=flowed" @@ -69,7 +69,7 @@ app.Get("/", func(c *fiber.Ctx) error { ```go title="Example 4" // Accept: text/plain;format=flowed;q=0.9, text/plain // i.e., "I prefer text/plain;format=flowed less than other forms of text/plain" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Beware: the order in which offers are listed matters. // Although the client specified they prefer not to receive format=flowed, // the text/plain Accept matches with "text/plain;format=flowed" first, so it is returned. @@ -87,7 +87,7 @@ Fiber provides similar functions for the other accept headers. // Accept-Encoding: gzip, compress;q=0.2 // Accept-Language: en;q=0.8, nl, ru -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.AcceptsCharsets("utf-16", "iso-8859-1") // "iso-8859-1" @@ -111,14 +111,14 @@ func (c *Ctx) AllParams() map[string]string ```go title="Example" // GET http://example.com/user/fenny -app.Get("/user/:name", func(c *fiber.Ctx) error { +app.Get("/user/:name", func(c fiber.Ctx) error { c.AllParams() // "{"name": "fenny"}" // ... }) // GET http://example.com/user/fenny/123 -app.Get("/user/*", func(c *fiber.Ctx) error { +app.Get("/user/*", func(c fiber.Ctx) error { c.AllParams() // "{"*1": "fenny/123"}" // ... @@ -134,7 +134,7 @@ func (c *Ctx) App() *App ``` ```go title="Example" -app.Get("/stack", func(c *fiber.Ctx) error { +app.Get("/stack", func(c fiber.Ctx) error { return c.JSON(c.App().Stack()) }) ``` @@ -152,7 +152,7 @@ func (c *Ctx) Append(field string, values ...string) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Append("Link", "http://google.com", "http://localhost") // => Link: http://localhost, http://google.com @@ -172,7 +172,7 @@ func (c *Ctx) Attachment(filename ...string) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Attachment() // => Content-Disposition: attachment @@ -184,6 +184,47 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` +## AutoFormat + +Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. It uses [Accepts](ctx.md#accepts) to select a proper format. +The supported content types are `text/html`, `text/plain`, `application/json`, and `application/xml`. +For more flexible content negotiation, use [Format](ctx.md#format). + + +:::info +If the header is **not** specified or there is **no** proper format, **text/plain** is used. +::: + +```go title="Signature" +func (c *Ctx) AutoFormat(body any) error +``` + +```go title="Example" +app.Get("/", func(c fiber.Ctx) error { + // Accept: text/plain + c.AutoFormat("Hello, World!") + // => Hello, World! + + // Accept: text/html + c.AutoFormat("Hello, World!") + // =>

Hello, World!

+ + type User struct { + Name string + } + user := User{"John Doe"} + + // Accept: application/json + c.AutoFormat(user) + // => {"Name":"John Doe"} + + // Accept: application/xml + c.AutoFormat(user) + // => John Doe + // .. +}) +``` + ## BaseURL Returns the base URL \(**protocol** + **host**\) as a `string`. @@ -195,7 +236,7 @@ func (c *Ctx) BaseURL() string ```go title="Example" // GET https://example.com/page#chapter-1 -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.BaseURL() // https://example.com // ... }) @@ -211,13 +252,13 @@ func (c *Ctx) Bind(vars Map) error ``` ```go title="Example" -app.Use(func(c *fiber.Ctx) error { +app.Use(func(c fiber.Ctx) error { c.Bind(fiber.Map{ "Title": "Hello, World!", }) }) -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.Render("xxx.tmpl", fiber.Map{}) // Render will use Title variable }) ``` @@ -233,7 +274,7 @@ func (c *Ctx) BodyRaw() []byte ```go title="Example" // curl -X POST http://localhost:8080 -d user=john -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Get raw body from POST request: return c.Send(c.BodyRaw()) // []byte("user=john") }) @@ -253,7 +294,7 @@ func (c *Ctx) Body() []byte ```go title="Example" // echo 'user=john' | gzip | curl -v -i --data-binary @- -H "Content-Encoding: gzip" http://localhost:8080 -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Decompress body from POST request based on the Content-Encoding and return the raw content: return c.Send(c.Body()) // []byte("user=john") }) @@ -277,7 +318,7 @@ It is important to specify the correct struct tag based on the content type to b | `text/xml` | xml | ```go title="Signature" -func (c *Ctx) BodyParser(out interface{}) error +func (c *Ctx) BodyParser(out any) error ``` ```go title="Example" @@ -287,7 +328,7 @@ type Person struct { Pass string `json:"pass" xml:"pass" form:"pass"` } -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { p := new(Person) if err := c.BodyParser(p); err != nil { @@ -325,7 +366,7 @@ func (c *Ctx) ClearCookie(key ...string) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Clears all cookies: c.ClearCookie() @@ -343,7 +384,7 @@ Web browsers and other compliant clients will only clear the cookie if the given ::: ```go title="Example" -app.Get("/set", func(c *fiber.Ctx) error { +app.Get("/set", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "token", Value: "randomvalue", @@ -355,7 +396,7 @@ app.Get("/set", func(c *fiber.Ctx) error { // ... }) -app.Get("/delete", func(c *fiber.Ctx) error { +app.Get("/delete", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "token", // Set expiry date to the past @@ -379,7 +420,7 @@ func (c *Ctx) ClientHelloInfo() *tls.ClientHelloInfo ```go title="Example" // GET http://example.com/hello -app.Get("/hello", func(c *fiber.Ctx) error { +app.Get("/hello", func(c fiber.Ctx) error { chi := c.ClientHelloInfo() // ... }) @@ -421,7 +462,7 @@ type Cookie struct { ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Create cookie cookie := new(fiber.Cookie) cookie.Name = "john" @@ -440,7 +481,7 @@ This method is similar to [BodyParser](ctx.md#bodyparser), but for cookie parame It is important to use the struct tag "cookie". For example, if you want to parse a cookie with a field called Age, you would use a struct field of `cookie:"age"`. ```go title="Signature" -func (c *Ctx) CookieParser(out interface{}) error +func (c *Ctx) CookieParser(out any) error ``` ```go title="Example" @@ -451,7 +492,7 @@ type Person struct { Job bool `cookie:"job"` } -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.CookieParser(p); err != nil { @@ -475,7 +516,7 @@ func (c *Ctx) Cookies(key string, defaultValue ...string) string ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Get cookie by key: c.Cookies("name") // "john" c.Cookies("empty", "doe") // "doe" @@ -499,7 +540,7 @@ func (c *Ctx) Download(file string, filename ...string) error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.Download("./files/report-12345.pdf"); // => Download report-12345.pdf @@ -510,30 +551,54 @@ app.Get("/", func(c *fiber.Ctx) error { ## Format -Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. It uses [Accepts](ctx.md#accepts) to select a proper format. +Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. It uses [Accepts](ctx.md#accepts) to select a proper format from the supplied offers. A default handler can be provided by setting the `MediaType` to `"default"`. If no offers match and no default is provided, a 406 (Not Acceptable) response is sent. The Content-Type is automatically set when a handler is selected. :::info -If the header is **not** specified or there is **no** proper format, **text/plain** is used. +If the Accept header is **not** specified, the first handler will be used. ::: ```go title="Signature" -func (c *Ctx) Format(body interface{}) error +func (c *Ctx) Format(handlers ...ResFmt) error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { - // Accept: text/plain - c.Format("Hello, World!") - // => Hello, World! +// Accept: application/json => {"command":"eat","subject":"fruit"} +// Accept: text/plain => Eat Fruit! +// Accept: application/xml => Not Acceptable +app.Get("/no-default", func(c fiber.Ctx) error { + return c.Format( + fiber.ResFmt{"application/json", func(c fiber.Ctx) error { + return c.JSON(fiber.Map{ + "command": "eat", + "subject": "fruit", + }) + }}, + fiber.ResFmt{"text/plain", func(c fiber.Ctx) error { + return c.SendString("Eat Fruit!") + }}, + ) +}) - // Accept: text/html - c.Format("Hello, World!") - // =>

Hello, World!

+// Accept: application/json => {"command":"eat","subject":"fruit"} +// Accept: text/plain => Eat Fruit! +// Accept: application/xml => Eat Fruit! +app.Get("/default", func(c fiber.Ctx) error { + textHandler := func(c fiber.Ctx) error { + return c.SendString("Eat Fruit!") + } - // Accept: application/json - c.Format("Hello, World!") - // => "Hello, World!" - // .. + handlers := []fiber.ResFmt{ + {"application/json", func(c fiber.Ctx) error { + return c.JSON(fiber.Map{ + "command": "eat", + "subject": "fruit", + }) + }}, + {"text/plain", textHandler}, + {"default", textHandler}, + } + + return c.Format(handlers...) }) ``` @@ -546,7 +611,7 @@ func (c *Ctx) FormFile(key string) (*multipart.FileHeader, error) ``` ```go title="Example" -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Get first file from form field "document": file, err := c.FormFile("document") @@ -564,7 +629,7 @@ func (c *Ctx) FormValue(key string, defaultValue ...string) string ``` ```go title="Example" -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Get first value from form field "name": c.FormValue("name") // => "john" or "" if not exist @@ -601,7 +666,7 @@ func (c *Ctx) Get(key string, defaultValue ...string) string ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Get("Content-Type") // "text/plain" c.Get("CoNtEnT-TypE") // "text/plain" c.Get("something", "john") // "john" @@ -636,7 +701,7 @@ func (c *Ctx) GetRespHeader(key string, defaultValue ...string) string ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.GetRespHeader("X-Request-Id") // "8d7ad5e3-aaf3-450b-a241-2beb887efd54" c.GetRespHeader("Content-Type") // "text/plain" c.GetRespHeader("something", "john") // "john" @@ -667,15 +732,15 @@ func (c *Ctx) GetRouteURL(routeName string, params Map) (string, error) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Home page") }).Name("home") -app.Get("/user/:id", func(c *fiber.Ctx) error { +app.Get("/user/:id", func(c fiber.Ctx) error { return c.SendString(c.Params("id")) }).Name("user.show") -app.Get("/test", func(c *fiber.Ctx) error { +app.Get("/test", func(c fiber.Ctx) error { location, _ := c.GetRouteURL("user.show", fiber.Map{"id": 1}) return c.SendString(location) }) @@ -694,7 +759,7 @@ func (c *Ctx) Hostname() string ```go title="Example" // GET http://google.com/search -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Hostname() // "google.com" // ... @@ -713,7 +778,7 @@ func (c *Ctx) IP() string ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.IP() // "127.0.0.1" // ... @@ -739,7 +804,7 @@ func (c *Ctx) IPs() []string ```go title="Example" // X-Forwarded-For: proxy1, 127.0.0.1, proxy3 -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.IPs() // ["proxy1", "127.0.0.1", "proxy3"] // ... @@ -765,7 +830,7 @@ func (c *Ctx) Is(extension string) bool ```go title="Example" // Content-Type: text/html; charset=utf-8 -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Is("html") // true c.Is(".html") // true c.Is("json") // false @@ -784,7 +849,7 @@ func (c *Ctx) IsFromLocal() bool { ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // If request came from localhost, return true else return false c.IsFromLocal() @@ -801,7 +866,7 @@ JSON also sets the content header to the `ctype` parameter. If no `ctype` is pas ::: ```go title="Signature" -func (c *Ctx) JSON(data interface{}, ctype ...string) error +func (c *Ctx) JSON(data any, ctype ...string) error ``` ```go title="Example" @@ -810,7 +875,7 @@ type SomeStruct struct { Age uint8 } -app.Get("/json", func(c *fiber.Ctx) error { +app.Get("/json", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", @@ -853,7 +918,7 @@ Sends a JSON response with JSONP support. This method is identical to [JSON](ctx Override this by passing a **named string** in the method. ```go title="Signature" -func (c *Ctx) JSONP(data interface{}, callback ...string) error +func (c *Ctx) JSONP(data any, callback ...string) error ``` ```go title="Example" @@ -862,7 +927,7 @@ type SomeStruct struct { age uint8 } -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ name: "Grame", @@ -886,7 +951,7 @@ func (c *Ctx) Links(link ...string) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Links( "http://api.example.com/users?page=2", "next", "http://api.example.com/users?page=5", "last", @@ -907,17 +972,27 @@ This is useful if you want to pass some **specific** data to the next middleware ::: ```go title="Signature" -func (c *Ctx) Locals(key interface{}, value ...interface{}) interface{} +func (c *Ctx) Locals(key any, value ...any) any ``` ```go title="Example" -app.Use(func(c *fiber.Ctx) error { - c.Locals("user", "admin") + +// key is an unexported type for keys defined in this package. +// This prevents collisions with keys defined in other packages. +type key int + +// userKey is the key for user.User values in Contexts. It is +// unexported; clients use user.NewContext and user.FromContext +// instead of using this key directly. +var userKey key + +app.Use(func(c fiber.Ctx) error { + c.Locals(userKey, "admin") return c.Next() }) -app.Get("/admin", func(c *fiber.Ctx) error { - if c.Locals("user") == "admin" { +app.Get("/admin", func(c fiber.Ctx) error { + if c.Locals(userKey) == "admin" { return c.Status(fiber.StatusOK).SendString("Welcome, admin!") } return c.SendStatus(fiber.StatusForbidden) @@ -925,6 +1000,31 @@ app.Get("/admin", func(c *fiber.Ctx) error { }) ``` +An alternative version of the Locals method that takes advantage of Go's generics feature is also available. This version +allows for the manipulation and retrieval of local values within a request's context with a more specific data type. + +```go title="Signature" +func Locals[V any](c Ctx, key any, value ...V) V +``` + +```go title="Example" +app.Use(func(c Ctx) error { + fiber.Locals[string](c, "john", "doe") + fiber.Locals[int](c, "age", 18) + fiber.Locals[bool](c, "isHuman", true) + return c.Next() +}) +app.Get("/test", func(c Ctx) error { + fiber.Locals[string](c, "john") // "doe" + fiber.Locals[int](c, "age") // 18 + fiber.Locals[bool](c, "isHuman") // true + return nil +}) +```` + +Make sure to understand and correctly implement the Locals method in both its standard and generic form for better control +over route-specific data within your application. + ## Location Sets the response [Location](https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Location) HTTP header to the specified path parameter. @@ -934,7 +1034,7 @@ func (c *Ctx) Location(path string) ``` ```go title="Example" -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { c.Location("http://example.com") c.Location("/foo/bar") @@ -953,7 +1053,7 @@ func (c *Ctx) Method(override ...string) string ``` ```go title="Example" -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { c.Method() // "POST" c.Method("GET") @@ -972,7 +1072,7 @@ func (c *Ctx) MultipartForm() (*multipart.Form, error) ``` ```go title="Example" -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Parse the multipart form: if form, err := c.MultipartForm(); err == nil { // => *multipart.Form @@ -1011,17 +1111,17 @@ func (c *Ctx) Next() error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { fmt.Println("1st route!") return c.Next() }) -app.Get("*", func(c *fiber.Ctx) error { +app.Get("*", func(c fiber.Ctx) error { fmt.Println("2nd route!") return c.Next() }) -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { fmt.Println("3rd route!") return c.SendString("Hello, World!") }) @@ -1038,7 +1138,7 @@ func (c *Ctx) OriginalURL() string ```go title="Example" // GET http://example.com/search?q=something -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.OriginalURL() // "/search?q=something" // ... @@ -1062,14 +1162,14 @@ func (c *Ctx) Params(key string, defaultValue ...string) string ```go title="Example" // GET http://example.com/user/fenny -app.Get("/user/:name", func(c *fiber.Ctx) error { +app.Get("/user/:name", func(c fiber.Ctx) error { c.Params("name") // "fenny" // ... }) // GET http://example.com/user/fenny/123 -app.Get("/user/*", func(c *fiber.Ctx) error { +app.Get("/user/*", func(c fiber.Ctx) error { c.Params("*") // "fenny/123" c.Params("*1") // "fenny/123" @@ -1089,7 +1189,7 @@ c.Params("*2") // "blue/xs" For reasons of **downward compatibility**, the first parameter segment for the parameter character can also be accessed without the counter. ```go title="Example" -app.Get("/v1/*/shop/*", func(c *fiber.Ctx) error { +app.Get("/v1/*/shop/*", func(c fiber.Ctx) error { c.Params("*") // outputs the values of the first wildcard segment }) ``` @@ -1114,7 +1214,7 @@ func (c *Ctx) ParamsInt(key string) (int, error) ```go title="Example" // GET http://example.com/user/123 -app.Get("/user/:id", func(c *fiber.Ctx) error { +app.Get("/user/:id", func(c fiber.Ctx) error { id, err := c.ParamsInt("id") // int 123 and no error // ... @@ -1129,12 +1229,12 @@ This method is equivalent of using `atoi` with ctx.Params This method is similar to BodyParser, but for path parameters. It is important to use the struct tag "params". For example, if you want to parse a path parameter with a field called Pass, you would use a struct field of params:"pass" ```go title="Signature" -func (c *Ctx) ParamsParser(out interface{}) error +func (c *Ctx) ParamsParser(out any) error ``` ```go title="Example" // GET http://example.com/user/111 -app.Get("/user/:id", func(c *fiber.Ctx) error { +app.Get("/user/:id", func(c fiber.Ctx) error { param := struct {ID uint `params:"id"`}{} c.ParamsParser(¶m) // "{"id": 111}" @@ -1155,7 +1255,7 @@ func (c *Ctx) Path(override ...string) string ```go title="Example" // GET http://example.com/users?sort=desc -app.Get("/users", func(c *fiber.Ctx) error { +app.Get("/users", func(c fiber.Ctx) error { c.Path() // "/users" c.Path("/john") @@ -1176,7 +1276,7 @@ func (c *Ctx) Protocol() string ```go title="Example" // GET http://example.com -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Protocol() // "http" // ... @@ -1194,7 +1294,7 @@ func (c *Ctx) Queries() map[string]string ```go title="Example" // GET http://example.com/?name=alex&want_pizza=false&id= -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["name"] // "alex" m["want_pizza"] // "false" @@ -1206,7 +1306,7 @@ app.Get("/", func(c *fiber.Ctx) error { ```go title="Example" // GET http://example.com/?field1=value1&field1=value2&field2=value3 -app.Get("/", func (c *fiber.Ctx) error { +app.Get("/", func (c fiber.Ctx) error { m := c.Queries() m["field1"] // "value2" m["field2"] // value3 @@ -1216,7 +1316,7 @@ app.Get("/", func (c *fiber.Ctx) error { ```go title="Example" // GET http://example.com/?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3 -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["list_a"] // "3" m["list_b[]"] // "3" @@ -1227,7 +1327,7 @@ app.Get("/", func(c *fiber.Ctx) error { ```go title="Example" // GET /api/posts?filters.author.name=John&filters.category.name=Technology -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["filters.author.name"] // John m["filters.category.name"] // Technology @@ -1237,7 +1337,7 @@ app.Get("/", func(c *fiber.Ctx) error { ```go title="Example" // GET /api/posts?tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { m := c.Queries() m["tags"] // apple,orange,banana m["filters[tags]"] // apple,orange,banana @@ -1262,7 +1362,7 @@ func (c *Ctx) Query(key string, defaultValue ...string) string ```go title="Example" // GET http://example.com/?order=desc&brand=nike -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Query("order") // "desc" c.Query("brand") // "nike" c.Query("empty", "nike") // "nike" @@ -1274,94 +1374,42 @@ app.Get("/", func(c *fiber.Ctx) error { > _Returned value is only valid within the handler. Do not store any references. > Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) -## QueryBool +In certain scenarios, it can be useful to have an alternative approach to handle different types of query parameters, not +just strings. This can be achieved using a generic Query function known as `Query[V QueryType](c Ctx, key string, defaultValue ...V) V`. +This function is capable of parsing a query string and returning a value of a type that is assumed and specified by `V QueryType`. -This property is an object containing a property for each query boolean parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. - -:::caution -Please note if that parameter is not in the request, false will be returned. -If the parameter is not a boolean, it is still tried to be converted and usually returned as false. -::: +Here is the signature for the generic Query function: ```go title="Signature" -func (c *Ctx) QueryBool(key string, defaultValue ...bool) bool -``` - -```go title="Example" -// GET http://example.com/?name=alex&want_pizza=false&id= - -app.Get("/", func(c *fiber.Ctx) error { - c.QueryBool("want_pizza") // false - c.QueryBool("want_pizza", true) // false - c.QueryBool("name") // false - c.QueryBool("name", true) // true - c.QueryBool("id") // false - c.QueryBool("id", true) // true - - // ... -}) +func Query[V QueryType](c Ctx, key string, defaultValue ...V) V ``` -## QueryFloat - -This property is an object containing a property for each query float64 parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. - -:::caution -Please note if that parameter is not in the request, zero will be returned. -If the parameter is not a number, it is still tried to be converted and usually returned as 1. -::: - -:::info -Defaults to the float64 zero \(`0`\), if the param **doesn't** exist. -::: - -```go title="Signature" -func (c *Ctx) QueryFloat(key string, defaultValue ...float64) float64 -``` +Consider this example: ```go title="Example" -// GET http://example.com/?name=alex&amount=32.23&id= +// GET http://example.com/?page=1&brand=nike&new=true -app.Get("/", func(c *fiber.Ctx) error { - c.QueryFloat("amount") // 32.23 - c.QueryFloat("amount", 3) // 32.23 - c.QueryFloat("name", 1) // 1 - c.QueryFloat("name") // 0 - c.QueryFloat("id", 3) // 3 +app.Get("/", func(c fiber.Ctx) error { + fiber.Query[int](c, "page") // 1 + fiber.Query[string](c, "brand") // "nike" + fiber.Query[bool](c, "new") // true // ... }) ``` -## QueryInt - -This property is an object containing a property for each query integer parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. - -:::caution -Please note if that parameter is not in the request, zero will be returned. -If the parameter is not a number, it is still tried to be converted and usually returned as 1. -::: - -:::info -Defaults to the integer zero \(`0`\), if the param **doesn't** exist. -::: - -```go title="Signature" -func (c *Ctx) QueryInt(key string, defaultValue ...int) int -``` - -```go title="Example" -// GET http://example.com/?name=alex&wanna_cake=2&id= - -app.Get("/", func(c *fiber.Ctx) error { - c.QueryInt("wanna_cake", 1) // 2 - c.QueryInt("name", 1) // 1 - c.QueryInt("id", 1) // 1 - c.QueryInt("id") // 0 +In this case, `Query[V QueryType](c Ctx, key string, defaultValue ...V) V` can retrieve 'page' as an integer, 'brand' +as a string, and 'new' as a boolean. The function uses the appropriate parsing function for each specified type to ensure +the correct type is returned. This simplifies the retrieval process of different types of query parameters, making your +controller actions cleaner. - // ... -}) -``` +The generic Query function supports returning the following data types based on V QueryType: +- Integer: int, int8, int16, int32, int64 +- Unsigned integer: uint, uint8, uint16, uint32, uint64 +- Floating-point numbers: float32, float64 +- Boolean: bool +- String: string +- Byte array: []byte ## QueryParser @@ -1369,7 +1417,7 @@ This method is similar to [BodyParser](ctx.md#bodyparser), but for query paramet It is important to use the struct tag "query". For example, if you want to parse a query parameter with a field called Pass, you would use a struct field of `query:"pass"`. ```go title="Signature" -func (c *Ctx) QueryParser(out interface{}) error +func (c *Ctx) QueryParser(out any) error ``` ```go title="Example" @@ -1380,7 +1428,7 @@ type Person struct { Products []string `query:"products"` } -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.QueryParser(p); err != nil { @@ -1389,11 +1437,7 @@ app.Get("/", func(c *fiber.Ctx) error { log.Println(p.Name) // john log.Println(p.Pass) // doe - // fiber.Config{EnableSplittingOnParsers: false} - default - log.Println(p.Products) // ["shoe,hat"] - // fiber.Config{EnableSplittingOnParsers: true} - // log.Println(p.Products) // ["shoe", "hat"] - + log.Println(p.Products) // [shoe, hat] // ... }) @@ -1402,10 +1446,6 @@ app.Get("/", func(c *fiber.Ctx) error { // curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat" ``` -:::info -For more parser settings please look here [Config](fiber.md#config) -::: - ## Range A struct containing the type and a slice of ranges will be returned. @@ -1416,7 +1456,7 @@ func (c *Ctx) Range(size int) (Range, error) ```go title="Example" // Range: bytes=500-700, 700-900 -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { b := c.Range(1000) if b.Type == "bytes" { for r := range r.Ranges { @@ -1440,17 +1480,17 @@ func (c *Ctx) Redirect(location string, status ...int) error ``` ```go title="Example" -app.Get("/coffee", func(c *fiber.Ctx) error { +app.Get("/coffee", func(c fiber.Ctx) error { return c.Redirect("/teapot") }) -app.Get("/teapot", func(c *fiber.Ctx) error { +app.Get("/teapot", func(c fiber.Ctx) error { return c.Status(fiber.StatusTeapot).Send("🍵 short and stout 🍵") }) ``` ```go title="More examples" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.Redirect("/foo/bar") return c.Redirect("../login") return c.Redirect("http://example.com") @@ -1475,14 +1515,14 @@ func (c *Ctx) RedirectToRoute(routeName string, params fiber.Map, status ...int) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // /user/fiber return c.RedirectToRoute("user", fiber.Map{ "name": "fiber" }) }) -app.Get("/with-queries", func(c *fiber.Ctx) error { +app.Get("/with-queries", func(c fiber.Ctx) error { // /user/fiber?data[0][name]=john&data[0][age]=10&test=doe return c.RedirectToRoute("user", fiber.Map{ "name": "fiber", @@ -1490,7 +1530,7 @@ app.Get("/with-queries", func(c *fiber.Ctx) error { }) }) -app.Get("/user/:name", func(c *fiber.Ctx) error { +app.Get("/user/:name", func(c fiber.Ctx) error { return c.SendString(c.Params("name")) }).Name("user") ``` @@ -1508,15 +1548,15 @@ func (c *Ctx) RedirectBack(fallback string, status ...int) error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Home page") }) -app.Get("/test", func(c *fiber.Ctx) error { +app.Get("/test", func(c fiber.Ctx) error { c.Set("Content-Type", "text/html") return c.SendString(`Back`) }) -app.Get("/back", func(c *fiber.Ctx) error { +app.Get("/back", func(c fiber.Ctx) error { return c.RedirectBack("/") }) ``` @@ -1526,7 +1566,7 @@ app.Get("/back", func(c *fiber.Ctx) error { Renders a view with data and sends a `text/html` response. By default `Render` uses the default [**Go Template engine**](https://pkg.go.dev/html/template/). If you want to use another View engine, please take a look at our [**Template middleware**](https://docs.gofiber.io/template). ```go title="Signature" -func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error +func (c *Ctx) Render(name string, bind any, layouts ...string) error ``` ## Request @@ -1538,7 +1578,7 @@ func (c *Ctx) Request() *fasthttp.Request ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Request().Header.Method() // => []byte("GET") }) @@ -1550,7 +1590,7 @@ This method is similar to [BodyParser](ctx.md#bodyparser), but for request heade It is important to use the struct tag "reqHeader". For example, if you want to parse a request header with a field called Pass, you would use a struct field of `reqHeader:"pass"`. ```go title="Signature" -func (c *Ctx) ReqHeaderParser(out interface{}) error +func (c *Ctx) ReqHeaderParser(out any) error ``` ```go title="Example" @@ -1561,7 +1601,7 @@ type Person struct { Products []string `reqHeader:"products"` } -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { p := new(Person) if err := c.ReqHeaderParser(p); err != nil { @@ -1588,7 +1628,7 @@ func (c *Ctx) Response() *fasthttp.Response ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Response().BodyWriter().Write([]byte("Hello, World!")) // => "Hello, World!" return nil @@ -1604,11 +1644,11 @@ func (c *Ctx) RestartRouting() error ``` ```go title="Example" -app.Get("/new", func(c *fiber.Ctx) error { +app.Get("/new", func(c fiber.Ctx) error { return c.SendString("From /new") }) -app.Get("/old", func(c *fiber.Ctx) error { +app.Get("/old", func(c fiber.Ctx) error { c.Path("/new") return c.RestartRouting() }) @@ -1626,7 +1666,7 @@ func (c *Ctx) Route() *Route // http://localhost:8080/hello -app.Get("/hello/:name", func(c *fiber.Ctx) error { +app.Get("/hello/:name", func(c fiber.Ctx) error { r := c.Route() fmt.Println(r.Method, r.Path, r.Params, r.Handlers) // GET /hello/:name handler [name] @@ -1641,7 +1681,7 @@ Do not rely on `c.Route()` in middlewares **before** calling `c.Next()` - `c.Rou ```go title="Example" func MyMiddleware() fiber.Handler { - return func(c *fiber.Ctx) error { + return func(c fiber.Ctx) error { beforeNext := c.Route().Path // Will be '/' err := c.Next() afterNext := c.Route().Path // Will be '/hello/:name' @@ -1659,7 +1699,7 @@ func (c *Ctx) SaveFile(fh *multipart.FileHeader, path string) error ``` ```go title="Example" -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Parse the multipart form: if form, err := c.MultipartForm(); err == nil { // => *multipart.Form @@ -1694,7 +1734,7 @@ func (c *Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, s ```go title="Example" storage := memory.New() -app.Post("/", func(c *fiber.Ctx) error { +app.Post("/", func(c fiber.Ctx) error { // Parse the multipart form: if form, err := c.MultipartForm(); err == nil { // => *multipart.Form @@ -1740,7 +1780,7 @@ func (c *Ctx) Send(body []byte) error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.Send([]byte("Hello, World!")) // => "Hello, World!" }) ``` @@ -1757,7 +1797,7 @@ func (c *Ctx) SendStream(stream io.Reader, size ...int) error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") // => "Hello, World!" @@ -1779,7 +1819,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error ``` ```go title="Example" -app.Get("/not-found", func(c *fiber.Ctx) error { +app.Get("/not-found", func(c fiber.Ctx) error { return c.SendFile("./public/404.html"); // Disable compression @@ -1792,7 +1832,7 @@ If the file contains an url specific character you have to escape it before pass ::: ```go title="Example" -app.Get("/file-with-url-chars", func(c *fiber.Ctx) error { +app.Get("/file-with-url-chars", func(c fiber.Ctx) error { return c.SendFile(url.PathEscape("hash_sign_#.txt")) }) ``` @@ -1814,7 +1854,7 @@ func (c *Ctx) SendStatus(status int) error ``` ```go title="Example" -app.Get("/not-found", func(c *fiber.Ctx) error { +app.Get("/not-found", func(c fiber.Ctx) error { return c.SendStatus(415) // => 415 "Unsupported Media Type" @@ -1833,7 +1873,7 @@ func (c *Ctx) Set(key string, val string) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Set("Content-Type", "text/plain") // => "Content-type: text/plain" @@ -1849,7 +1889,7 @@ Allow you to config BodyParser/QueryParser decoder, base on schema's options, pr func SetParserDecoder(parserConfig fiber.ParserConfig{ IgnoreUnknownKeys bool, ParserType []fiber.ParserType{ - Customtype interface{}, + Customtype any, Converter func(string) reflect.Value, }, ZeroEmpty bool, @@ -1895,14 +1935,14 @@ type Demo struct { Body string `form:"body" query:"body"` } -app.Post("/body", func(c *fiber.Ctx) error { +app.Post("/body", func(c fiber.Ctx) error { var d Demo c.BodyParser(&d) fmt.Println("d.Date", d.Date.String()) return c.JSON(d) }) -app.Get("/query", func(c *fiber.Ctx) error { +app.Get("/query", func(c fiber.Ctx) error { var d Demo c.QueryParser(&d) fmt.Println("d.Date", d.Date.String()) @@ -1924,7 +1964,7 @@ func (c *Ctx) SetUserContext(ctx context.Context) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { ctx := context.Background() c.SetUserContext(ctx) // Here ctx could be any context implementation @@ -1954,16 +1994,16 @@ func (c *Ctx) Status(status int) *Ctx ``` ```go title="Example" -app.Get("/fiber", func(c *fiber.Ctx) error { +app.Get("/fiber", func(c fiber.Ctx) error { c.Status(fiber.StatusOK) return nil } -app.Get("/hello", func(c *fiber.Ctx) error { +app.Get("/hello", func(c fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("Bad Request") } -app.Get("/world", func(c *fiber.Ctx) error { +app.Get("/world", func(c fiber.Ctx) error { return c.Status(fiber.StatusNotFound).SendFile("./public/gopher.png") }) ``` @@ -1981,7 +2021,7 @@ func (c *Ctx) Subdomains(offset ...int) []string ```go title="Example" // Host: "tobi.ferrets.example.com" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Subdomains() // ["ferrets", "tobi"] c.Subdomains(1) // ["tobi"] @@ -1998,7 +2038,7 @@ func (c *Ctx) Type(ext string, charset ...string) *Ctx ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Type(".html") // => "text/html" c.Type("html") // => "text/html" c.Type("png") // => "image/png" @@ -2019,7 +2059,7 @@ func (c *Ctx) UserContext() context.Context ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { ctx := c.UserContext() // ctx is context implementation set by user @@ -2040,7 +2080,7 @@ func (c *Ctx) Vary(fields ...string) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Vary("Origin") // => Vary: Origin c.Vary("User-Agent") // => Vary: Origin, User-Agent @@ -2063,7 +2103,7 @@ func (c *Ctx) Write(p []byte) (n int, err error) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Write([]byte("Hello, World!")) // => "Hello, World!" fmt.Fprintf(c, "%s\n", "Hello, World!") // "Hello, World!Hello, World!" @@ -2075,11 +2115,11 @@ app.Get("/", func(c *fiber.Ctx) error { Writef adopts the string with variables ```go title="Signature" -func (c *Ctx) Writef(f string, a ...interface{}) (n int, err error) +func (c *Ctx) Writef(f string, a ...any) (n int, err error) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { world := "World!" c.Writef("Hello, %s", world) // => "Hello, World!" @@ -2096,7 +2136,7 @@ func (c *Ctx) WriteString(s string) (n int, err error) ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.WriteString("Hello, World!") // => "Hello, World!" fmt.Fprintf(c, "%s\n", "Hello, World!") // "Hello, World!Hello, World!" @@ -2114,7 +2154,7 @@ func (c *Ctx) XHR() bool ```go title="Example" // X-Requested-With: XMLHttpRequest -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.XHR() // true // ... @@ -2130,7 +2170,7 @@ XML also sets the content header to **application/xml**. ::: ```go title="Signature" -func (c *Ctx) XML(data interface{}) error +func (c *Ctx) XML(data any) error ``` ```go title="Example" @@ -2140,7 +2180,7 @@ type SomeStruct struct { Age uint8 `xml:"Age"` } -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Create data struct: data := SomeStruct{ Name: "Grame", diff --git a/versioned_docs/version-v2.x/api/fiber.md b/versioned_docs/version-v2.x/api/fiber.md index 1f9a91b8bf6..28850db0ba1 100644 --- a/versioned_docs/version-v2.x/api/fiber.md +++ b/versioned_docs/version-v2.x/api/fiber.md @@ -72,7 +72,7 @@ app := fiber.New(fiber.Config{ | ReadTimeout | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` | | RequestMethods | `[]string` | RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` | | ServerHeader | `string` | Enables the `Server` HTTP header with the given value. | `""` | -| StreamRequestBody | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger then the current limit. | `false` | +| StreamRequestBody | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` | | StrictRouting | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` | | TrustedProxies | `[]string` | Contains the list of trusted proxy IP's. Look at `EnableTrustedProxyCheck` doc.

It can take IP or IP range addresses. If it gets IP range, it iterates all possible addresses. | `[]string*__*` | | UnescapePath | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` | @@ -91,7 +91,7 @@ func NewError(code int, message ...string) *Error ``` ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return fiber.NewError(782, "Custom error message") }) ``` diff --git a/versioned_docs/version-v2.x/api/log.md b/versioned_docs/version-v2.x/api/log.md index 9b741b13f7a..e53d6d4b0a4 100644 --- a/versioned_docs/version-v2.x/api/log.md +++ b/versioned_docs/version-v2.x/api/log.md @@ -80,7 +80,7 @@ log.Fatalw("", "fruit", "banana") If you are in a project and just want to use a simple log function that can be printed at any time in the global, we provide a global log. ```go -import "github.com/gofiber/fiber/v2/log" +import "github.com/gofiber/fiber/v3/log" log.Info("info") log.Warn("warn") @@ -92,7 +92,7 @@ You can also find an already implemented adaptation under contrib, or use your o ```go import ( "log" - fiberlog "github.com/gofiber/fiber/v2/log" + fiberlog "github.com/gofiber/fiber/v3/log" ) var _ log.AllLogger = (*customLogger)(nil) @@ -113,7 +113,7 @@ The default logger is LevelTrace. Note that this method is not **concurrent-safe**. ```go -import "github.com/gofiber/fiber/v2/log" +import "github.com/gofiber/fiber/v3/log" log.SetLevel(log.LevelInfo) ``` diff --git a/versioned_docs/version-v2.x/api/middleware/adaptor.md b/versioned_docs/version-v2.x/api/middleware/adaptor.md index 64df229ce22..39fc1895aff 100644 --- a/versioned_docs/version-v2.x/api/middleware/adaptor.md +++ b/versioned_docs/version-v2.x/api/middleware/adaptor.md @@ -15,8 +15,8 @@ Converter for net/http handlers to/from Fiber request handlers, special thanks t | FiberHandler | `FiberHandler(h fiber.Handler) http.Handler` | fiber.Handler -> http.Handler | FiberHandlerFunc | `FiberHandlerFunc(h fiber.Handler) http.HandlerFunc` | fiber.Handler -> http.HandlerFunc | FiberApp | `FiberApp(app *fiber.App) http.HandlerFunc` | Fiber app -> http.HandlerFunc -| ConvertRequest | `ConvertRequest(c *fiber.Ctx, forServer bool) (*http.Request, error)` | fiber.Ctx -> http.Request -| CopyContextToFiberContext | `CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx)` | context.Context -> fasthttp.RequestCtx +| ConvertRequest | `ConvertRequest(c fiber.Ctx, forServer bool) (*http.Request, error)` | fiber.Ctx -> http.Request +| CopyContextToFiberContext | `CopyContextToFiberContext(context any, requestContext *fasthttp.RequestCtx)` | context.Context -> fasthttp.RequestCtx ## Examples @@ -28,8 +28,8 @@ import ( "fmt" "net/http" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { @@ -63,8 +63,8 @@ import ( "log" "net/http" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { @@ -93,8 +93,8 @@ package main import ( "net/http" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { @@ -108,7 +108,7 @@ func main() { http.ListenAndServe(":3000", nil) } -func greet(c *fiber.Ctx) error { +func greet(c fiber.Ctx) error { return c.SendString("Hello World!") } ``` @@ -120,8 +120,8 @@ package main import ( "net/http" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { @@ -133,7 +133,7 @@ func main() { http.ListenAndServe(":3000", adaptor.FiberApp(app)) } -func greet(c *fiber.Ctx) error { +func greet(c fiber.Ctx) error { return c.SendString("Hello World!") } ``` @@ -145,8 +145,8 @@ package main import ( "net/http" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/adaptor" ) func main() { @@ -158,7 +158,7 @@ func main() { http.ListenAndServe(":3000", adaptor.FiberApp(app)) } -func greetWithHTTPReq(c *fiber.Ctx) error { +func greetWithHTTPReq(c fiber.Ctx) error { httpReq, err := adaptor.ConvertRequest(c, false) if err != nil { return err diff --git a/versioned_docs/version-v2.x/api/middleware/basicauth.md b/versioned_docs/version-v2.x/api/middleware/basicauth.md index d0f3609bdef..246945ce7b7 100644 --- a/versioned_docs/version-v2.x/api/middleware/basicauth.md +++ b/versioned_docs/version-v2.x/api/middleware/basicauth.md @@ -10,6 +10,8 @@ Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) th ```go func New(config Config) fiber.Handler +func UsernameFromContext(c fiber.Ctx) string +func PasswordFromContext(c fiber.Ctx) string ``` ## Examples @@ -18,8 +20,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/basicauth" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/basicauth" ) ``` @@ -50,25 +52,32 @@ app.Use(basicauth.New(basicauth.Config{ } return false }, - Unauthorized: func(c *fiber.Ctx) error { + Unauthorized: func(c fiber.Ctx) error { return c.SendFile("./unauthorized.html") }, - ContextUsername: "_user", - ContextPassword: "_pass", })) ``` +Getting the username and password + +```go +func handler(c fiber.Ctx) error { + username := basicauth.UsernameFromContext(c) + password := basicauth.PasswordFromContext(c) + log.Printf("Username: %s Password: %s", username, password) + return c.SendString("Hello, " + username) +} +``` + ## Config | Property | Type | Description | Default | |:----------------|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Users | `map[string]string` | Users defines the allowed credentials. | `map[string]string{}` | | Realm | `string` | Realm is a string to define the realm attribute of BasicAuth. The realm identifies the system to authenticate against and can be used by clients to save credentials. | `"Restricted"` | | Authorizer | `func(string, string) bool` | Authorizer defines a function to check the credentials. It will be called with a username and password and is expected to return true or false to indicate approval. | `nil` | | Unauthorized | `fiber.Handler` | Unauthorized defines the response body for unauthorized responses. | `nil` | -| ContextUsername | `interface{}` | ContextUsername is the key to store the username in Locals. | `"username"` | -| ContextPassword | `interface{}` | ContextPassword is the key to store the password in Locals. | `"password"` | ## Default Config @@ -79,7 +88,5 @@ var ConfigDefault = Config{ Realm: "Restricted", Authorizer: nil, Unauthorized: nil, - ContextUsername: "username", - ContextPassword: "password", } ``` diff --git a/versioned_docs/version-v2.x/api/middleware/cache.md b/versioned_docs/version-v2.x/api/middleware/cache.md index e0146941030..26022cf0f1f 100644 --- a/versioned_docs/version-v2.x/api/middleware/cache.md +++ b/versioned_docs/version-v2.x/api/middleware/cache.md @@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cache" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/cache" ) ``` @@ -35,8 +35,8 @@ app.Use(cache.New()) // Or extend your config for customization app.Use(cache.New(cache.Config{ - Next: func(c *fiber.Ctx) bool { - return c.Query("noCache") == "true" + Next: func(c fiber.Ctx) bool { + return fiber.Query[bool](c, "noCache") }, Expiration: 30 * time.Minute, CacheControl: true, @@ -47,16 +47,16 @@ Or you can custom key and expire time like this: ```go app.Use(cache.New(cache.Config{ - ExpirationGenerator: func(c *fiber.Ctx, cfg *cache.Config) time.Duration { + ExpirationGenerator: func(c fiber.Ctx, cfg *cache.Config) time.Duration { newCacheTime, _ := strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) return time.Second * time.Duration(newCacheTime) }, - KeyGenerator: func(c *fiber.Ctx) string { + KeyGenerator: func(c fiber.Ctx) string { return utils.CopyString(c.Path()) }, })) -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { c.Response().Header.Add("Cache-Time", "6000") return c.SendString("hi") }) @@ -66,15 +66,15 @@ app.Get("/", func(c *fiber.Ctx) error { | Property | Type | Description | Default | |:---------------------|:------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function that is executed before creating the cache entry and can be used to execute the request without cache creation. If an entry already exists, it will be used. If you want to completely bypass the cache functionality in certain cases, you should use the [skip middleware](./skip.md). | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function that is executed before creating the cache entry and can be used to execute the request without cache creation. If an entry already exists, it will be used. If you want to completely bypass the cache functionality in certain cases, you should use the [skip middleware](./skip.md). | `nil` | | Expiration | `time.Duration` | Expiration is the time that a cached response will live. | `1 * time.Minute` | | CacheHeader | `string` | CacheHeader is the header on the response header that indicates the cache status, with the possible return values "hit," "miss," or "unreachable." | `X-Cache` | | CacheControl | `bool` | CacheControl enables client-side caching if set to true. | `false` | -| KeyGenerator | `func(*fiber.Ctx) string` | Key allows you to generate custom keys. | `func(c *fiber.Ctx) string { return utils.CopyString(c.Path()) }` | -| ExpirationGenerator | `func(*fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request. | `nil` | +| KeyGenerator | `func(fiber.Ctx) string` | Key allows you to generate custom keys. | `func(c fiber.Ctx) string { return utils.CopyString(c.Path()) }` | +| ExpirationGenerator | `func(fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request. | `nil` | | Storage | `fiber.Storage` | Store is used to store the state of the middleware. | In-memory store | | Store (Deprecated) | `fiber.Storage` | Deprecated: Use Storage instead. | In-memory store | -| Key (Deprecated) | `func(*fiber.Ctx) string` | Deprecated: Use KeyGenerator instead. | `nil` | +| Key (Deprecated) | `func(fiber.Ctx) string` | Deprecated: Use KeyGenerator instead. | `nil` | | StoreResponseHeaders | `bool` | StoreResponseHeaders allows you to store additional headers generated by next middlewares & handler. | `false` | | MaxBytes | `uint` | MaxBytes is the maximum number of bytes of response bodies simultaneously stored in cache. | `0` (No limit) | | Methods | `[]string` | Methods specifies the HTTP methods to cache. | `[]string{fiber.MethodGet, fiber.MethodHead}` | @@ -87,7 +87,7 @@ var ConfigDefault = Config{ Expiration: 1 * time.Minute, CacheHeader: "X-Cache", CacheControl: false, - KeyGenerator: func(c *fiber.Ctx) string { + KeyGenerator: func(c fiber.Ctx) string { return utils.CopyString(c.Path()) }, ExpirationGenerator: nil, diff --git a/versioned_docs/version-v2.x/api/middleware/compress.md b/versioned_docs/version-v2.x/api/middleware/compress.md index 472f9d98043..063f000714d 100644 --- a/versioned_docs/version-v2.x/api/middleware/compress.md +++ b/versioned_docs/version-v2.x/api/middleware/compress.md @@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/compress" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/compress" ) ``` @@ -40,7 +40,7 @@ app.Use(compress.New(compress.Config{ // Skip middleware for specific routes app.Use(compress.New(compress.Config{ - Next: func(c *fiber.Ctx) bool { + Next: func(c fiber.Ctx) bool { return c.Path() == "/dont_compress" }, Level: compress.LevelBestSpeed, // 1 @@ -53,7 +53,7 @@ app.Use(compress.New(compress.Config{ | Property | Type | Description | Default | |:---------|:------------------------|:--------------------------------------------------------------------|:-------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Level | `Level` | Level determines the compression algorithm. | `LevelDefault (0)` | Possible values for the "Level" field are: diff --git a/versioned_docs/version-v2.x/api/middleware/cors.md b/versioned_docs/version-v2.x/api/middleware/cors.md index 9a28342fd35..95645d81725 100644 --- a/versioned_docs/version-v2.x/api/middleware/cors.md +++ b/versioned_docs/version-v2.x/api/middleware/cors.md @@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/cors" ) ``` @@ -60,7 +60,7 @@ app.Use(cors.New(cors.Config{ | Property | Type | Description | Default | |:-----------------|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. | `nil` | | AllowOrigins | `string` | AllowOrigin defines a comma separated list of origins that may access the resource. | `"*"` | | AllowMethods | `string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` | diff --git a/versioned_docs/version-v2.x/api/middleware/csrf.md b/versioned_docs/version-v2.x/api/middleware/csrf.md index 536e8a4b2d2..e6c1425e919 100644 --- a/versioned_docs/version-v2.x/api/middleware/csrf.md +++ b/versioned_docs/version-v2.x/api/middleware/csrf.md @@ -12,7 +12,7 @@ As a [Defense In Depth](#defense-in-depth) measure, this middleware performs [Re ## Token Generation -CSRF tokens are generated on 'safe' requests and when the existing token has expired or hasn't been set yet. If `SingleUseToken` is `true`, a new token is generated after each use. Retrieve the CSRF token using `c.Locals(contextKey)`, where `contextKey` is defined within the configuration. +CSRF tokens are generated on 'safe' requests and when the existing token has expired or hasn't been set yet. If `SingleUseToken` is `true`, a new token is generated after each use. Retrieve the CSRF token using `csrf.TokenFromContext(c)`. ## Security Considerations @@ -31,7 +31,7 @@ By default, the middleware generates and stores tokens using the `fiber.Storage` When the authorization status changes, the previously issued token MUST be deleted, and a new one generated. See [Token Lifecycle](#token-lifecycle) [Deleting Tokens](#deleting-tokens) for more information. :::caution -When using this pattern, it's important to set the `CookieSameSite` option to `Lax` or `Strict` and ensure that the Extractor is not `CsrfFromCookie`, and KeyLookup is not `cookie:`. +When using this pattern, it's important to set the `CookieSameSite` option to `Lax` or `Strict` and ensure that the Extractor is not `FromCookie`, and KeyLookup is not `cookie:`. ::: :::note @@ -86,7 +86,8 @@ Using `SingleUseToken` comes with usability trade-offs and is not enabled by def When the authorization status changes, the CSRF token MUST be deleted, and a new one generated. This can be done by calling `handler.DeleteToken(c)`. ```go -if handler, ok := app.AcquireCtx(ctx).Locals(csrf.ConfigDefault.HandlerContextKey).(*CSRFHandler); ok { +handler := csrf.HandlerFromContext(ctx) +if handler != nil { if err := handler.DeleteToken(app.AcquireCtx(ctx)); err != nil { // handle error } @@ -105,6 +106,10 @@ It's important to note that the token is sent as a header on every request. If y ```go func New(config ...Config) fiber.Handler +func TokenFromContext(c fiber.Ctx) string +func HandlerFromContext(c fiber.Ctx) *Handler + +func (h *Handler) DeleteToken(c fiber.Ctx) error ``` ## Examples @@ -113,8 +118,8 @@ Import the middleware package that is part of the Fiber web framework: ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/csrf" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/csrf" ) ``` @@ -131,15 +136,48 @@ app.Use(csrf.New(csrf.Config{ CookieSameSite: "Lax", Expiration: 1 * time.Hour, KeyGenerator: utils.UUIDv4, + Extractor: func(c fiber.Ctx) (string, error) { ... }, })) ``` +:::info +KeyLookup will be ignored if Extractor is explicitly set. +::: + +Getting the CSRF token in a handler: + +```go +func handler(c fiber.Ctx) error { + handler := csrf.HandlerFromContext(c) + token := csrf.TokenFromContext(c) + if handler == nil { + panic("csrf middleware handler not registered") + } + cfg := handler.Config + if cfg == nil { + panic("csrf middleware handler has no config") + } + if !strings.Contains(cfg.KeyLookup, ":") { + panic("invalid KeyLookup format") + } + formKey := strings.Split(cfg.KeyLookup, ":")[1] + + tmpl := fmt.Sprintf(`
+ + + +
`, formKey, token) + c.Set("Content-Type", "text/html") + return c.SendString(tmpl) +} +``` + ## Config | Property | Type | Description | Default | |:------------------|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to create an Extractor that extracts the token from the request. Possible values: "`header:`", "`query:`", "`param:`", "`form:`", "`cookie:`". Ignored if an Extractor is explicitly set. | "header:X-Csrf-Token" | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to create an Extractor that extracts the token from the request. Possible values: "`header:`", "`query:`", "`param:`", "`form:`", "`cookie:`". Ignored if an Extractor is explicitly set. | "header:X-CSRF-Token" | | CookieName | `string` | Name of the csrf cookie. This cookie will store the csrf key. | "csrf_" | | CookieDomain | `string` | Domain of the CSRF cookie. | "" | | CookiePath | `string` | Path of the CSRF cookie. | "" | @@ -151,15 +189,10 @@ app.Use(csrf.New(csrf.Config{ | SingleUseToken | `bool` | SingleUseToken indicates if the CSRF token be destroyed and a new one generated on each use. (See TokenLifecycle) | false | | Storage | `fiber.Storage` | Store is used to store the state of the middleware. | `nil` | | Session | `*session.Store` | Session is used to store the state of the middleware. Overrides Storage if set. | `nil` | -| SessionKey | `string` | SessionKey is the key used to store the token within the session. | "fiber.csrf.token" | -| ContextKey | `inteface{}` | Context key to store the generated CSRF token into the context. If left empty, the token will not be stored within the context. | "" | +| SessionKey | `string` | SessionKey is the key used to store the token in the session. | "csrfToken" | | KeyGenerator | `func() string` | KeyGenerator creates a new CSRF token. | utils.UUID | -| CookieExpires | `time.Duration` (Deprecated) | Deprecated: Please use Expiration. | 0 | -| Cookie | `*fiber.Cookie` (Deprecated) | Deprecated: Please use Cookie* related fields. | `nil` | -| TokenLookup | `string` (Deprecated) | Deprecated: Please use KeyLookup. | "" | | ErrorHandler | `fiber.ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. | DefaultErrorHandler | -| Extractor | `func(*fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | -| HandlerContextKey | `interface{}` | HandlerContextKey is used to store the CSRF Handler into context. | "fiber.csrf.handler" | +| Extractor | `func(fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | ### Default Config @@ -171,9 +204,8 @@ var ConfigDefault = Config{ Expiration: 1 * time.Hour, KeyGenerator: utils.UUIDv4, ErrorHandler: defaultErrorHandler, - Extractor: CsrfFromHeader(HeaderName), - SessionKey: "fiber.csrf.token", - HandlerContextKey: "fiber.csrf.handler", + Extractor: FromHeader(HeaderName), + SessionKey: "csrfToken", } ``` @@ -192,10 +224,9 @@ var ConfigDefault = Config{ Expiration: 1 * time.Hour, KeyGenerator: utils.UUIDv4, ErrorHandler: defaultErrorHandler, - Extractor: CsrfFromHeader(HeaderName), + Extractor: FromHeader(HeaderName), Session: session.Store, - SessionKey: "fiber.csrf.token", - HandlerContextKey: "fiber.csrf.handler", + SessionKey: "csrfToken", } ``` @@ -228,7 +259,7 @@ Example, returning a JSON response for API requests and rendering an error page ```go app.Use(csrf.New(csrf.Config{ - ErrorHandler: func(c *fiber.Ctx, err error) error { + ErrorHandler: func(c fiber.Ctx, err error) error { accepts := c.Accepts("html", "json") path := c.Path() if accepts == "json" || strings.HasPrefix(path, "/api/") { diff --git a/versioned_docs/version-v2.x/api/middleware/earlydata.md b/versioned_docs/version-v2.x/api/middleware/earlydata.md index 50e5bb174c5..74a41375139 100644 --- a/versioned_docs/version-v2.x/api/middleware/earlydata.md +++ b/versioned_docs/version-v2.x/api/middleware/earlydata.md @@ -21,6 +21,7 @@ Safe HTTP methods — `GET`, `HEAD`, `OPTIONS` and `TRACE` — should not modify ```go func New(config ...Config) fiber.Handler +func IsEarlyData(c fiber.Ctx) bool ``` ## Examples @@ -29,8 +30,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/earlydata" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/earlydata" ) ``` @@ -49,22 +50,55 @@ app.Use(earlydata.New(earlydata.Config{ ## Config +<<<<<<< HEAD:middleware/earlydata/README.md +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c fiber.Ctx) bool + + // IsEarlyData returns whether the request is an early-data request. + // + // Optional. Default: a function which checks if the "Early-Data" request header equals "1". + IsEarlyData func(c fiber.Ctx) bool + + // AllowEarlyData returns whether the early-data request should be allowed or rejected. + // + // Optional. Default: a function which rejects the request on unsafe and allows the request on safe HTTP request methods. + AllowEarlyData func(c fiber.Ctx) bool + + // Error is returned in case an early-data request is rejected. + // + // Optional. Default: fiber.ErrTooEarly. + Error error +} +``` + +### Default Config + +```go +var ConfigDefault = Config{ + IsEarlyData: func(c fiber.Ctx) bool { + return c.Get("Early-Data") == "1" +======= | Property | Type | Description | Default | |:---------------|:------------------------|:-------------------------------------------------------------------------------------|:-------------------------------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| IsEarlyData | `func(*fiber.Ctx) bool` | IsEarlyData returns whether the request is an early-data request. | Function checking if "Early-Data" header equals "1" | -| AllowEarlyData | `func(*fiber.Ctx) bool` | AllowEarlyData returns whether the early-data request should be allowed or rejected. | Function rejecting on unsafe and allowing safe methods | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| IsEarlyData | `func(fiber.Ctx) bool` | IsEarlyData returns whether the request is an early-data request. | Function checking if "Early-Data" header equals "1" | +| AllowEarlyData | `func(fiber.Ctx) bool` | AllowEarlyData returns whether the early-data request should be allowed or rejected. | Function rejecting on unsafe and allowing safe methods | | Error | `error` | Error is returned in case an early-data request is rejected. | `fiber.ErrTooEarly` | ## Default Config ```go var ConfigDefault = Config{ - IsEarlyData: func(c *fiber.Ctx) bool { + IsEarlyData: func(c fiber.Ctx) bool { return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue +>>>>>>> origin/master:docs/api/middleware/earlydata.md }, - AllowEarlyData: func(c *fiber.Ctx) bool { + AllowEarlyData: func(c fiber.Ctx) bool { return fiber.IsMethodSafe(c.Method()) }, diff --git a/versioned_docs/version-v2.x/api/middleware/encryptcookie.md b/versioned_docs/version-v2.x/api/middleware/encryptcookie.md index c6e689e0e80..39dca0cec65 100644 --- a/versioned_docs/version-v2.x/api/middleware/encryptcookie.md +++ b/versioned_docs/version-v2.x/api/middleware/encryptcookie.md @@ -4,11 +4,7 @@ id: encryptcookie # Encrypt Cookie -Encrypt Cookie is a middleware for [Fiber](https://github.com/gofiber/fiber) that secures your cookie values through encryption. - -:::note -This middleware encrypts cookie values and not the cookie names. -::: +Encrypt middleware for [Fiber](https://github.com/gofiber/fiber) which encrypts cookie values. Note: this middleware does not encrypt cookie names. ## Signatures @@ -22,30 +18,33 @@ func GenerateKey() string ## Examples -To use the Encrypt Cookie middleware, first, import the middleware package as part of the Fiber web framework: +Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/encryptcookie" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/encryptcookie" ) ``` -Once you've imported the middleware package, you can use it inside your Fiber app: +After you initiate your Fiber app, you can use the following possibilities: ```go -// Provide a minimal configuration +// Provide a minimal config +// `Key` must be a 32 character string. It's used to encrypt the values, so make sure it is random and keep it secret. +// You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. +// Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-thirty-2-character-string", })) -// Retrieve the encrypted cookie value -app.Get("/", func(c *fiber.Ctx) error { +// Get / reading out the encrypted cookie +app.Get("/", func(c fiber.Ctx) error { return c.SendString("value=" + c.Cookies("test")) }) -// Create an encrypted cookie -app.Post("/", func(c *fiber.Ctx) error { +// Post / create the encrypted cookie +app.Post("/", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "test", Value: "SomeThing", @@ -54,48 +53,71 @@ app.Post("/", func(c *fiber.Ctx) error { }) ``` -:::note -`Key` must be a 32 character string. It's used to encrypt the values, so make sure it is random and keep it secret. -You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. -Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. -::: - ## Config -| Property | Type | Description | Default | -|:----------|:----------------------------------------------------|:------------------------------------------------------------------------------------------------------|:-----------------------------| -| Next | `func(*fiber.Ctx) bool` | A function to skip this middleware when returned true. | `nil` | -| Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` | -| Key | `string` | A base64-encoded unique key to encode & decode cookies. Required. Key length should be 32 characters. | (No default, required field) | -| Encryptor | `func(decryptedString, key string) (string, error)` | A custom function to encrypt cookies. | `EncryptCookie` | -| Decryptor | `func(encryptedString, key string) (string, error)` | A custom function to decrypt cookies. | `DecryptCookie` | +<<<<<<< HEAD:middleware/encryptcookie/README.md +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c fiber.Ctx) bool + + // Array of cookie keys that should not be encrypted. + // + // Optional. Default: ["csrf_"] + Except []string + + // Base64 encoded unique key to encode & decode cookies. + // + // Required. The key should be 32 bytes of random data in base64-encoded form. + // You may run `openssl rand -base64 32` or use `encryptcookie.GenerateKey()` to generate a new key. + Key string + + // Custom function to encrypt cookies. + // + // Optional. Default: EncryptCookie + Encryptor func(decryptedString, key string) (string, error) + + // Custom function to decrypt cookies. + // + // Optional. Default: DecryptCookie + Decryptor func(encryptedString, key string) (string, error) +} +``` +======= +| Property | Type | Description | Default | +|:----------|:----------------------------------------------------|:----------------------------------------------------------------------------------------------------|:-----------------------------| +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` | +| Key | `string` | Base64 encoded unique key to encode & decode cookies. Required. Key length should be 32 characters. | (No default, required field) | +| Encryptor | `func(decryptedString, key string) (string, error)` | Custom function to encrypt cookies. | `EncryptCookie` | +| Decryptor | `func(encryptedString, key string) (string, error)` | Custom function to decrypt cookies. | `DecryptCookie` | +>>>>>>> origin/master:docs/api/middleware/encryptcookie.md ## Default Config ```go var ConfigDefault = Config{ Next: nil, - Except: []string{}, + Except: []string{"csrf_"}, Key: "", Encryptor: EncryptCookie, Decryptor: DecryptCookie, } ``` -## Usage With Other Middlewares That Reads Or Modify Cookies -Place the encryptcookie middleware before any other middleware that reads or modifies cookies. For example, if you are using the CSRF middleware, ensure that the encryptcookie middleware is placed before it. Failure to do so may prevent the CSRF middleware from reading the encrypted cookie. - -You may also choose to exclude certain cookies from encryption. For instance, if you are using the CSRF middleware with a frontend framework like Angular, and the framework reads the token from a cookie, you should exclude that cookie from encryption. This can be achieved by adding the cookie name to the Except array in the configuration: +## Usage of CSRF and Encryptcookie Middlewares with Custom Cookie Names +Normally, encryptcookie middleware skips `csrf_` cookies. However, it won't work when you use custom cookie names for CSRF. You should update `Except` config to avoid this problem. For example: ```go app.Use(encryptcookie.New(encryptcookie.Config{ - Key: "secret-thirty-2-character-string", - Except: []string{csrf.ConfigDefault.CookieName}, // exclude CSRF cookie + Key: "secret-thirty-2-character-string", + Except: []string{"csrf_1"}, // exclude CSRF cookie })) app.Use(csrf.New(csrf.Config{ - KeyLookup: "header:" + csrf.HeaderName, - CookieSameSite: "Lax", - CookieSecure: true, - CookieHTTPOnly: false, + KeyLookup: "form:test", + CookieName: "csrf_1", + CookieHTTPOnly: true, })) ``` diff --git a/versioned_docs/version-v2.x/api/middleware/envvar.md b/versioned_docs/version-v2.x/api/middleware/envvar.md index 1d9f4742974..3740c38a9f0 100644 --- a/versioned_docs/version-v2.x/api/middleware/envvar.md +++ b/versioned_docs/version-v2.x/api/middleware/envvar.md @@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/envvar" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/envvar" ) ``` diff --git a/versioned_docs/version-v2.x/api/middleware/etag.md b/versioned_docs/version-v2.x/api/middleware/etag.md index 24be273021b..28cf69195e8 100644 --- a/versioned_docs/version-v2.x/api/middleware/etag.md +++ b/versioned_docs/version-v2.x/api/middleware/etag.md @@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/etag" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/etag" ) ``` @@ -30,7 +30,7 @@ After you initiate your Fiber app, you can use the following possibilities: app.Use(etag.New()) // Get / receives Etag: "13-1831710635" in response header -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) @@ -40,7 +40,7 @@ app.Use(etag.New(etag.Config{ })) // Get / receives Etag: "W/"13-1831710635" in response header -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) ``` @@ -50,7 +50,7 @@ app.Get("/", func(c *fiber.Ctx) error { | Property | Type | Description | Default | |:---------|:------------------------|:-------------------------------------------------------------------------------------------------------------------|:--------| | Weak | `bool` | Weak indicates that a weak validator is used. Weak etags are easy to generate but are less useful for comparisons. | `false` | -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | ## Default Config diff --git a/versioned_docs/version-v2.x/api/middleware/expvar.md b/versioned_docs/version-v2.x/api/middleware/expvar.md index 900850e3647..8299b8fd443 100644 --- a/versioned_docs/version-v2.x/api/middleware/expvar.md +++ b/versioned_docs/version-v2.x/api/middleware/expvar.md @@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - expvarmw "github.com/gofiber/fiber/v2/middleware/expvar" + "github.com/gofiber/fiber/v3" + expvarmw "github.com/gofiber/fiber/v3/middleware/expvar" ) ``` @@ -28,7 +28,7 @@ After you initiate your Fiber app, you can use the following possibilities: var count = expvar.NewInt("count") app.Use(expvarmw.New()) -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { count.Add(1) return c.SendString(fmt.Sprintf("hello expvar count %d", count.Value())) @@ -61,7 +61,7 @@ curl 127.0.0.1:3000/debug/vars?r=c | Property | Type | Description | Default | |:---------|:------------------------|:--------------------------------------------------------------------|:--------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | ## Default Config diff --git a/versioned_docs/version-v2.x/api/middleware/favicon.md b/versioned_docs/version-v2.x/api/middleware/favicon.md index b1a9b0df6c0..ddb0a7a7dd5 100644 --- a/versioned_docs/version-v2.x/api/middleware/favicon.md +++ b/versioned_docs/version-v2.x/api/middleware/favicon.md @@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/favicon" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/favicon" ) ``` @@ -44,7 +44,7 @@ app.Use(favicon.New(favicon.Config{ | Property | Type | Description | Default | |:-------------|:------------------------|:---------------------------------------------------------------------------------|:---------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Data | `[]byte` | Raw data of the favicon file. This can be used instead of `File`. | `nil` | | File | `string` | File holds the path to an actual favicon that will be cached. | "" | | URL | `string` | URL for favicon handler. | "/favicon.ico" | diff --git a/versioned_docs/version-v2.x/api/middleware/filesystem.md b/versioned_docs/version-v2.x/api/middleware/filesystem.md index bbeff14bf74..01d50270ffb 100644 --- a/versioned_docs/version-v2.x/api/middleware/filesystem.md +++ b/versioned_docs/version-v2.x/api/middleware/filesystem.md @@ -24,8 +24,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" ) ``` @@ -63,8 +63,8 @@ import ( "log" "net/http" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" ) // Embed a single file @@ -103,8 +103,8 @@ func main() { package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" "github.com/markbates/pkger" ) @@ -128,8 +128,8 @@ func main() { package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" "github.com/gobuffalo/packr/v2" ) @@ -153,8 +153,8 @@ func main() { package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" "github.com/GeertJohan/go.rice" ) @@ -178,8 +178,8 @@ func main() { package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" "/myEmbeddedFiles" ) @@ -203,8 +203,8 @@ func main() { package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" // Use blank to invoke init function and register data to statik _ "/statik" @@ -231,7 +231,7 @@ func main() { | Property | Type | Description | Default | |:-------------------|:------------------------|:------------------------------------------------------------------------------------------------------------|:-------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Root | `http.FileSystem` | Root is a FileSystem that provides access to a collection of files and directories. | `nil` | | PathPrefix | `string` | PathPrefix defines a prefix to be added to a filepath when reading a file from the FileSystem. | "" | | Browse | `bool` | Enable directory browsing. | `false` | @@ -261,20 +261,20 @@ var ConfigDefault = Config{ Serves a file from an [HTTP file system](https://pkg.go.dev/net/http#FileSystem) at the specified path. ```go title="Signature" title="Signature" -func SendFile(c *fiber.Ctx, filesystem http.FileSystem, path string) error +func SendFile(c fiber.Ctx, filesystem http.FileSystem, path string) error ``` Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/filesystem" ) ``` ```go title="Example" // Define a route to serve a specific file -app.Get("/download", func(c *fiber.Ctx) error { +app.Get("/download", func(c fiber.Ctx) error { // Serve the file using SendFile function err := filesystem.SendFile(c, http.Dir("your/filesystem/root"), "path/to/your/file.txt") if err != nil { @@ -294,7 +294,7 @@ app.Use("/", filesystem.New(filesystem.Config{ })) // For all other routes (wildcard "*"), serve the "index.html" file from the "build" directory. -app.Use("*", func(ctx *fiber.Ctx) error { +app.Use("*", func(ctx fiber.Ctx) error { return filesystem.SendFile(ctx, http.FS(f), "build/index.html") }) ``` diff --git a/versioned_docs/version-v2.x/api/middleware/helmet.md b/versioned_docs/version-v2.x/api/middleware/helmet.md index 0835f31166f..b0dca52d544 100644 --- a/versioned_docs/version-v2.x/api/middleware/helmet.md +++ b/versioned_docs/version-v2.x/api/middleware/helmet.md @@ -17,8 +17,8 @@ func New(config ...Config) fiber.Handler package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/helmet" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/helmet" ) func main() { @@ -26,7 +26,7 @@ func main() { app.Use(helmet.New()) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { return c.SendString("Welcome!") }) @@ -44,7 +44,7 @@ curl -I http://localhost:3000 | Property | Type | Description | Default | |:--------------------------|:------------------------|:--------------------------------------------|:-----------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip middleware. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip middleware. | `nil` | | XSSProtection | `string` | XSSProtection | "0" | | ContentTypeNosniff | `string` | ContentTypeNosniff | "nosniff" | | XFrameOptions | `string` | XFrameOptions | "SAMEORIGIN" | diff --git a/versioned_docs/version-v2.x/api/middleware/idempotency.md b/versioned_docs/version-v2.x/api/middleware/idempotency.md index bab7c0e4503..475ec9dfc38 100644 --- a/versioned_docs/version-v2.x/api/middleware/idempotency.md +++ b/versioned_docs/version-v2.x/api/middleware/idempotency.md @@ -12,6 +12,8 @@ Refer to https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-ke ```go func New(config ...Config) fiber.Handler +func IsFromCache(c fiber.Ctx) bool +func WasPutToCache(c fiber.Ctx) bool ``` ## Examples @@ -20,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/idempotency" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/idempotency" ) ``` @@ -46,7 +48,7 @@ app.Use(idempotency.New(idempotency.Config{ | Property | Type | Description | Default | |:--------------------|:------------------------|:-----------------------------------------------------------------------------------------|:-------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | A function for safe methods | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | A function for safe methods | | Lifetime | `time.Duration` | Lifetime is the maximum lifetime of an idempotency key. | 30 * time.Minute | | KeyHeader | `string` | KeyHeader is the name of the header that contains the idempotency key. | "X-Idempotency-Key" | | KeyHeaderValidate | `func(string) error` | KeyHeaderValidate defines a function to validate the syntax of the idempotency header. | A function for UUID validation | @@ -58,7 +60,7 @@ app.Use(idempotency.New(idempotency.Config{ ```go var ConfigDefault = Config{ - Next: func(c *fiber.Ctx) bool { + Next: func(c fiber.Ctx) bool { // Skip middleware if the request was done using a safe HTTP method return fiber.IsMethodSafe(c.Method()) }, diff --git a/versioned_docs/version-v2.x/api/middleware/keyauth.md b/versioned_docs/version-v2.x/api/middleware/keyauth.md index 1a719c134d0..0699108588c 100644 --- a/versioned_docs/version-v2.x/api/middleware/keyauth.md +++ b/versioned_docs/version-v2.x/api/middleware/keyauth.md @@ -10,6 +10,7 @@ Key auth middleware provides a key based authentication. ```go func New(config ...Config) fiber.Handler +func TokenFromContext(c fiber.Ctx) string ``` ## Examples @@ -20,15 +21,15 @@ package main import ( "crypto/sha256" "crypto/subtle" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/keyauth" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/keyauth" ) var ( apiKey = "correct horse battery staple" ) -func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { +func validateAPIKey(c fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(apiKey)) hashedKey := sha256.Sum256([]byte(key)) @@ -47,7 +48,7 @@ func main() { Validator: validateAPIKey, })) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { return c.SendString("Successfully authenticated!") }) @@ -82,8 +83,8 @@ package main import ( "crypto/sha256" "crypto/subtle" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/keyauth" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/keyauth" "regexp" "strings" ) @@ -96,7 +97,7 @@ var ( } ) -func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { +func validateAPIKey(c fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(apiKey)) hashedKey := sha256.Sum256([]byte(key)) @@ -106,7 +107,7 @@ func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { return false, keyauth.ErrMissingOrMalformedAPIKey } -func authFilter(c *fiber.Ctx) bool { +func authFilter(c fiber.Ctx) bool { originalURL := strings.ToLower(c.OriginalURL()) for _, pattern := range protectedURLs { @@ -126,13 +127,13 @@ func main() { Validator: validateAPIKey, })) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { return c.SendString("Welcome") }) - app.Get("/authenticated", func(c *fiber.Ctx) error { + app.Get("/authenticated", func(c fiber.Ctx) error { return c.SendString("Successfully authenticated!") }) - app.Get("/auth2", func(c *fiber.Ctx) error { + app.Get("/auth2", func(c fiber.Ctx) error { return c.SendString("Successfully authenticated 2!") }) @@ -164,8 +165,8 @@ package main import ( "crypto/sha256" "crypto/subtle" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/keyauth" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/keyauth" ) const ( @@ -176,7 +177,7 @@ func main() { app := fiber.New() authMiddleware := keyauth.New(keyauth.Config{ - Validator: func(c *fiber.Ctx, key string) (bool, error) { + Validator: func(c fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(apiKey)) hashedKey := sha256.Sum256([]byte(key)) @@ -187,11 +188,11 @@ func main() { }, }) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { return c.SendString("Welcome") }) - app.Get("/allowed", authMiddleware, func(c *fiber.Ctx) error { + app.Get("/allowed", authMiddleware, func(c fiber.Ctx) error { return c.SendString("Successfully authenticated!") }) @@ -213,24 +214,23 @@ curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000 ## Config -| Property | Type | Description | Default | -|:---------------|:-----------------------------------------|:-----------------------------------------------------------------------------------------------------|:------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `nil` | -| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. | `401 Invalid or expired key` | +| Property | Type | Description | Default | +|:---------------|:-----------------------------------------|:-------------------------------------------------------------------------------------------------------|:------------------------------| +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `nil` | +| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. | `401 Invalid or expired key` | | KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to extract key from the request. | "header:Authorization" | -| AuthScheme | `string` | AuthScheme to be used in the Authorization header. | "Bearer" | -| Validator | `func(*fiber.Ctx, string) (bool, error)` | Validator is a function to validate the key. | A function for key validation | -| ContextKey | `interface{}` | Context key to store the bearer token from the token into context. | "token" | +| AuthScheme | `string` | AuthScheme to be used in the Authorization header. | "Bearer" | +| Validator | `func(fiber.Ctx, string) (bool, error)` | Validator is a function to validate the key. | A function for key validation | ## Default Config ```go var ConfigDefault = Config{ - SuccessHandler: func(c *fiber.Ctx) error { + SuccessHandler: func(c fiber.Ctx) error { return c.Next() }, - ErrorHandler: func(c *fiber.Ctx, err error) error { + ErrorHandler: func(c fiber.Ctx, err error) error { if err == ErrMissingOrMalformedAPIKey { return c.Status(fiber.StatusUnauthorized).SendString(err.Error()) } @@ -238,6 +238,5 @@ var ConfigDefault = Config{ }, KeyLookup: "header:" + fiber.HeaderAuthorization, AuthScheme: "Bearer", - ContextKey: "token", } ``` diff --git a/versioned_docs/version-v2.x/api/middleware/limiter.md b/versioned_docs/version-v2.x/api/middleware/limiter.md index 8a48cbd14b7..30986251c44 100644 --- a/versioned_docs/version-v2.x/api/middleware/limiter.md +++ b/versioned_docs/version-v2.x/api/middleware/limiter.md @@ -26,8 +26,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/limiter" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/limiter" ) ``` @@ -39,15 +39,15 @@ app.Use(limiter.New()) // Or extend your config for customization app.Use(limiter.New(limiter.Config{ - Next: func(c *fiber.Ctx) bool { + Next: func(c fiber.Ctx) bool { return c.IP() == "127.0.0.1" }, Max: 20, Expiration: 30 * time.Second, - KeyGenerator: func(c *fiber.Ctx) string { + KeyGenerator: func(c fiber.Ctx) string { return c.Get("x-forwarded-for") }, - LimitReached: func(c *fiber.Ctx) error { + LimitReached: func(c fiber.Ctx) error { return c.SendFile("./toofast.html") }, Storage: myCustomStorage{}, @@ -78,9 +78,9 @@ rate = weightOfPreviousWindpw + current window's amount request. | Property | Type | Description | Default | |:-----------------------|:--------------------------|:--------------------------------------------------------------------------------------------|:-----------------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Max | `int` | Max number of recent connections during `Expiration` seconds before sending a 429 response. | 5 | -| KeyGenerator | `func(*fiber.Ctx) string` | KeyGenerator allows you to generate custom keys, by default c.IP() is used. | A function using c.IP() as the default | +| KeyGenerator | `func(fiber.Ctx) string` | KeyGenerator allows you to generate custom keys, by default c.IP() is used. | A function using c.IP() as the default | | Expiration | `time.Duration` | Expiration is the time on how long to keep records of requests in memory. | 1 * time.Minute | | LimitReached | `fiber.Handler` | LimitReached is called when a request hits the limit. | A function sending 429 response | | SkipFailedRequests | `bool` | When set to true, requests with StatusCode >= 400 won't be counted. | false | @@ -89,7 +89,7 @@ rate = weightOfPreviousWindpw + current window's amount request. | LimiterMiddleware | `LimiterHandler` | LimiterMiddleware is the struct that implements a limiter middleware. | A new Fixed Window Rate Limiter | | Duration (Deprecated) | `time.Duration` | Deprecated: Use Expiration instead | - | | Store (Deprecated) | `fiber.Storage` | Deprecated: Use Storage instead | - | -| Key (Deprecated) | `func(*fiber.Ctx) string` | Deprecated: Use KeyGenerator instead | - | +| Key (Deprecated) | `func(fiber.Ctx) string` | Deprecated: Use KeyGenerator instead | - | :::note A custom store can be used if it implements the `Storage` interface - more details and an example can be found in `store.go`. @@ -101,10 +101,10 @@ A custom store can be used if it implements the `Storage` interface - more detai var ConfigDefault = Config{ Max: 5, Expiration: 1 * time.Minute, - KeyGenerator: func(c *fiber.Ctx) string { + KeyGenerator: func(c fiber.Ctx) string { return c.IP() }, - LimitReached: func(c *fiber.Ctx) error { + LimitReached: func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusTooManyRequests) }, SkipFailedRequests: false, diff --git a/versioned_docs/version-v2.x/api/middleware/logger.md b/versioned_docs/version-v2.x/api/middleware/logger.md index 0a23441aa35..9208eb46772 100644 --- a/versioned_docs/version-v2.x/api/middleware/logger.md +++ b/versioned_docs/version-v2.x/api/middleware/logger.md @@ -16,8 +16,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" ) ``` @@ -65,7 +65,7 @@ app.Use(logger.New(logger.Config{ // Add Custom Tags app.Use(logger.New(logger.Config{ CustomTags: map[string]logger.LogFunc{ - "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { + "custom_tag": func(output logger.Buffer, c fiber.Ctx, data *logger.Data, extraParam string) (int, error) { return output.WriteString("it is a custom tag") }, }, @@ -75,7 +75,7 @@ app.Use(logger.New(logger.Config{ app.Use(logger.New(logger.Config{ TimeFormat: time.RFC3339Nano, TimeZone: "Asia/Shanghai", - Done: func(c *fiber.Ctx, logString []byte) { + Done: func(c fiber.Ctx, logString []byte) { if c.Response().StatusCode() != fiber.StatusOK { reporter.SendToSlack(logString) } @@ -88,31 +88,35 @@ app.Use(logger.New(logger.Config{ })) ``` +:::tip +Writing to os.File is goroutine-safe, but if you are using a custom Output that is not goroutine-safe, make sure to implement locking to properly serialize writes. +::: + ## Config ### Config -| Property | Type | Description | Default | -|:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:-------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| Done | `func(*fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Output, and pass the log string as parameter. | `nil` | -| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | -| Format | `string` | Format defines the logging tags. | `${time} | ${status} | ${latency} | ${ip} | ${method} | ${path} | ${error}\n` | -| TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | -| TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | -| TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | -| Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | -| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | -| enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | -| enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | -| timeZoneLocation | `*time.Location` | Internal field for the time zone location. (This is not a user-configurable field) | - | +| Property | Type | Description | Default | +|:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------| +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Done | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Output, and pass the log string as parameter. | `nil` | +| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | +| Format | `string` | Format defines the logging tags. | `[${time}] ${status} - ${latency} ${method} ${path}\n` | +| TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | +| TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | +| TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | +| Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | +| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | +| enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | +| enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | +| timeZoneLocation | `*time.Location` | Internal field for the time zone location. (This is not a user-configurable field) | - | ## Default Config ```go var ConfigDefault = Config{ Next: nil, Done: nil, - Format: "${time} | ${status} | ${latency} | ${ip} | ${method} | ${path} | ${error}\n", + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", TimeFormat: "15:04:05", TimeZone: "Local", TimeInterval: 500 * time.Millisecond, diff --git a/versioned_docs/version-v2.x/api/middleware/monitor.md b/versioned_docs/version-v2.x/api/middleware/monitor.md index cbac367ce45..b314c66bce7 100644 --- a/versioned_docs/version-v2.x/api/middleware/monitor.md +++ b/versioned_docs/version-v2.x/api/middleware/monitor.md @@ -24,8 +24,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/monitor" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/monitor" ) ``` @@ -54,7 +54,7 @@ You can also access the API endpoint with | Title | `string` | Metrics page title | "Fiber Monitor" | | Refresh | `time.Duration` | Refresh period | 3 seconds | | APIOnly | `bool` | Whether the service should expose only the monitoring API | false | -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | CustomHead | `string` | Custom HTML Code to Head Section(Before End) | empty | | FontURL | `string` | FontURL for specify font resource path or URL | "https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap" | | ChartJsURL | `string` | ChartJsURL for specify ChartJS library path or URL | "https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js" | diff --git a/versioned_docs/version-v2.x/api/middleware/pprof.md b/versioned_docs/version-v2.x/api/middleware/pprof.md index c4808f2c1d5..bbac32fd1f2 100644 --- a/versioned_docs/version-v2.x/api/middleware/pprof.md +++ b/versioned_docs/version-v2.x/api/middleware/pprof.md @@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/pprof" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/pprof" ) ``` @@ -41,7 +41,7 @@ app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) | Property | Type | Description | Default | |:---------|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------|:--------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Prefix | `string` | Prefix defines a URL prefix added before "/debug/pprof". Note that it should start with (but not end with) a slash. Example: "/federated-fiber" | "" | ## Default Config diff --git a/versioned_docs/version-v2.x/api/middleware/proxy.md b/versioned_docs/version-v2.x/api/middleware/proxy.md index e36654fe48a..930382323ef 100644 --- a/versioned_docs/version-v2.x/api/middleware/proxy.md +++ b/versioned_docs/version-v2.x/api/middleware/proxy.md @@ -14,13 +14,13 @@ func Balancer(config Config) fiber.Handler // Forward performs the given http request and fills the given http response. func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler // Do performs the given http request and fills the given http response. -func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error +func Do(c fiber.Ctx, addr string, clients ...*fasthttp.Client) error // DoRedirects performs the given http request and fills the given http response while following up to maxRedirectsCount redirects. -func DoRedirects(c *fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error +func DoRedirects(c fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error // DoDeadline performs the given request and waits for response until the given deadline. -func DoDeadline(c *fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error +func DoDeadline(c fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error // DoTimeout performs the given request and waits for response during the given timeout duration. -func DoTimeout(c *fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error +func DoTimeout(c fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error // DomainForward the given http request based on the given domain and fills the given http response func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler // BalancerForward performs the given http request based round robin balancer and fills the given http response @@ -33,8 +33,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/proxy" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/proxy" ) ``` @@ -42,8 +42,8 @@ After you initiate your Fiber app, you can use the following possibilities: ```go // if target https site uses a self-signed certificate, you should -// call WithTlsConfig before Do and Forward -proxy.WithTlsConfig(&tls.Config{ +// call WithTLSConfig before Do and Forward +proxy.WithTLSConfig(&tls.Config{ InsecureSkipVerify: true, }) // if you need to use global self-custom client, you should use proxy.WithClient. @@ -65,7 +65,7 @@ app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif", &fasthttp.Clien })) // Make request within handler -app.Get("/:id", func(c *fiber.Ctx) error { +app.Get("/:id", func(c fiber.Ctx) error { url := "https://i.imgur.com/"+c.Params("id")+".gif" if err := proxy.Do(c, url); err != nil { return err @@ -76,7 +76,7 @@ app.Get("/:id", func(c *fiber.Ctx) error { }) // Make proxy requests while following redirects -app.Get("/proxy", func(c *fiber.Ctx) error { +app.Get("/proxy", func(c fiber.Ctx) error { if err := proxy.DoRedirects(c, "http://google.com", 3); err != nil { return err } @@ -86,7 +86,7 @@ app.Get("/proxy", func(c *fiber.Ctx) error { }) // Make proxy requests and wait up to 5 seconds before timing out -app.Get("/proxy", func(c *fiber.Ctx) error { +app.Get("/proxy", func(c fiber.Ctx) error { if err := proxy.DoTimeout(c, "http://localhost:3000", time.Second * 5); err != nil { return err } @@ -96,7 +96,7 @@ app.Get("/proxy", func(c *fiber.Ctx) error { }) // Make proxy requests, timeout a minute from now -app.Get("/proxy", func(c *fiber.Ctx) error { +app.Get("/proxy", func(c fiber.Ctx) error { if err := proxy.DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { return err } @@ -121,11 +121,11 @@ app.Use(proxy.Balancer(proxy.Config{ "http://localhost:3002", "http://localhost:3003", }, - ModifyRequest: func(c *fiber.Ctx) error { + ModifyRequest: func(c fiber.Ctx) error { c.Request().Header.Add("X-Real-IP", c.IP()) return nil }, - ModifyResponse: func(c *fiber.Ctx) error { + ModifyResponse: func(c fiber.Ctx) error { c.Response().Header.Del(fiber.HeaderServer) return nil }, @@ -143,7 +143,7 @@ app.Use(proxy.BalancerForward([]string{ | Property | Type | Description | Default | |:----------------|:-----------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Servers | `[]string` | Servers defines a list of `://` HTTP servers, which are used in a round-robin manner. i.e.: "https://foobar.com, http://www.foobar.com" | (Required) | | ModifyRequest | `fiber.Handler` | ModifyRequest allows you to alter the request. | `nil` | | ModifyResponse | `fiber.Handler` | ModifyResponse allows you to alter the response. | `nil` | diff --git a/versioned_docs/version-v2.x/api/middleware/recover.md b/versioned_docs/version-v2.x/api/middleware/recover.md index 81f67fddbc5..8d0a601eac0 100644 --- a/versioned_docs/version-v2.x/api/middleware/recover.md +++ b/versioned_docs/version-v2.x/api/middleware/recover.md @@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/recover" ) ``` @@ -30,7 +30,7 @@ After you initiate your Fiber app, you can use the following possibilities: app.Use(recover.New()) // This panic will be caught by the middleware -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { panic("I'm an error") }) ``` @@ -39,9 +39,9 @@ app.Get("/", func(c *fiber.Ctx) error { | Property | Type | Description | Default | |:------------------|:--------------------------------|:--------------------------------------------------------------------|:-------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | EnableStackTrace | `bool` | EnableStackTrace enables handling stack trace. | `false` | -| StackTraceHandler | `func(*fiber.Ctx, interface{})` | StackTraceHandler defines a function to handle stack trace. | defaultStackTraceHandler | +| StackTraceHandler | `func(fiber.Ctx, any)` | StackTraceHandler defines a function to handle stack trace. | defaultStackTraceHandler | ## Default Config diff --git a/versioned_docs/version-v2.x/api/middleware/redirect.md b/versioned_docs/version-v2.x/api/middleware/redirect.md index 762aa0b5d30..c7976a7a0cf 100644 --- a/versioned_docs/version-v2.x/api/middleware/redirect.md +++ b/versioned_docs/version-v2.x/api/middleware/redirect.md @@ -18,8 +18,8 @@ func New(config ...Config) fiber.Handler package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/redirect" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/redirect" ) func main() { @@ -33,10 +33,10 @@ func main() { StatusCode: 301, })) - app.Get("/new", func(c *fiber.Ctx) error { + app.Get("/new", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) - app.Get("/new/*", func(c *fiber.Ctx) error { + app.Get("/new/*", func(c fiber.Ctx) error { return c.SendString("Wildcard: " + c.Params("*")) }) @@ -55,7 +55,7 @@ curl http://localhost:3000/old/hello | Property | Type | Description | Default | |:-----------|:------------------------|:---------------------------------------------------------------------------------------------------------------------------|:-----------------------| -| Next | `func(*fiber.Ctx) bool` | Filter defines a function to skip middleware. | `nil` | +| Next | `func(fiber.Ctx) bool` | Filter defines a function to skip middleware. | `nil` | | Rules | `map[string]string` | Rules defines the URL path rewrite rules. The values captured in asterisk can be retrieved by index e.g. $1, $2 and so on. | Required | | StatusCode | `int` | The status code when redirecting. This is ignored if Redirect is disabled. | 302 Temporary Redirect | diff --git a/versioned_docs/version-v2.x/api/middleware/requestid.md b/versioned_docs/version-v2.x/api/middleware/requestid.md index c8ca8d599e9..36abb201912 100644 --- a/versioned_docs/version-v2.x/api/middleware/requestid.md +++ b/versioned_docs/version-v2.x/api/middleware/requestid.md @@ -10,6 +10,7 @@ RequestID middleware for [Fiber](https://github.com/gofiber/fiber) that adds an ```go func New(config ...Config) fiber.Handler +func FromContext(c fiber.Ctx) string ``` ## Examples @@ -18,8 +19,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/requestid" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/requestid" ) ``` @@ -38,14 +39,23 @@ app.Use(requestid.New(requestid.Config{ })) ``` +Getting the request ID + +```go +func handler(c fiber.Ctx) error { + id := requestid.FromContext(c) + log.Printf("Request ID: %s", id) + return c.SendString("Hello, World!") +} +``` + ## Config | Property | Type | Description | Default | |:-----------|:------------------------|:--------------------------------------------------------------------------------------------------|:---------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | Header | `string` | Header is the header key where to get/set the unique request ID. | "X-Request-ID" | | Generator | `func() string` | Generator defines a function to generate the unique identifier. | utils.UUID | -| ContextKey | `interface{}` | ContextKey defines the key used when storing the request ID in the locals for a specific request. | "requestid" | ## Default Config The default config uses a fast UUID generator which will expose the number of @@ -57,6 +67,5 @@ var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, Generator: utils.UUID, - ContextKey: "requestid", } ``` diff --git a/versioned_docs/version-v2.x/api/middleware/rewrite.md b/versioned_docs/version-v2.x/api/middleware/rewrite.md index fd595958414..d7ca4a223f4 100644 --- a/versioned_docs/version-v2.x/api/middleware/rewrite.md +++ b/versioned_docs/version-v2.x/api/middleware/rewrite.md @@ -16,7 +16,7 @@ func New(config ...Config) fiber.Handler | Property | Type | Description | Default | |:---------|:------------------------|:-----------------------------------------------------------------------------------------------------|:-----------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip middleware. | `nil` | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip middleware. | `nil` | | Rules | `map[string]string` | Rules defines the URL path rewrite rules. The values captured in asterisk can be retrieved by index. | (Required) | ### Examples @@ -24,8 +24,8 @@ func New(config ...Config) fiber.Handler package main import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/rewrite" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/rewrite" ) func main() { @@ -38,10 +38,10 @@ func main() { }, })) - app.Get("/new", func(c *fiber.Ctx) error { + app.Get("/new", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) - app.Get("/new/*", func(c *fiber.Ctx) error { + app.Get("/new/*", func(c fiber.Ctx) error { return c.SendString("Wildcard: " + c.Params("*")) }) diff --git a/versioned_docs/version-v2.x/api/middleware/session.md b/versioned_docs/version-v2.x/api/middleware/session.md index 267ce8f99eb..db62b53cd47 100644 --- a/versioned_docs/version-v2.x/api/middleware/session.md +++ b/versioned_docs/version-v2.x/api/middleware/session.md @@ -14,13 +14,13 @@ This middleware uses our [Storage](https://github.com/gofiber/storage) package t ```go func New(config ...Config) *Store -func (s *Store) RegisterType(i interface{}) -func (s *Store) Get(c *fiber.Ctx) (*Session, error) +func (s *Store) RegisterType(i any) +func (s *Store) Get(c fiber.Ctx) (*Session, error) func (s *Store) Delete(id string) error func (s *Store) Reset() error -func (s *Session) Get(key string) interface{} -func (s *Session) Set(key string, val interface{}) +func (s *Session) Get(key string) any +func (s *Session) Set(key string, val any) func (s *Session) Delete(key string) func (s *Session) Destroy() error func (s *Session) Reset() error @@ -33,15 +33,15 @@ func (s *Session) SetExpiry(exp time.Duration) ``` :::caution -Storing `interface{}` values are limited to built-ins Go types. +Storing `any` values are limited to built-ins Go types. ::: ## Examples Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/session" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/session" ) ``` @@ -52,7 +52,7 @@ After you initiate your Fiber app, you can use the following possibilities: // This stores all of your app's sessions store := session.New() -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Get session from storage sess, err := store.Get(c) if err != nil { diff --git a/versioned_docs/version-v2.x/api/middleware/skip.md b/versioned_docs/version-v2.x/api/middleware/skip.md index 0923bd0ee26..07df603bc9f 100644 --- a/versioned_docs/version-v2.x/api/middleware/skip.md +++ b/versioned_docs/version-v2.x/api/middleware/skip.md @@ -8,15 +8,15 @@ Skip middleware for [Fiber](https://github.com/gofiber/fiber) that skips a wrapp ## Signatures ```go -func New(handler fiber.Handler, exclude func(c *fiber.Ctx) bool) fiber.Handler +func New(handler fiber.Handler, exclude func(c fiber.Ctx) bool) fiber.Handler ``` ## Examples Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/skip" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/skip" ) ``` @@ -26,18 +26,18 @@ After you initiate your Fiber app, you can use the following possibilities: func main() { app := fiber.New() - app.Use(skip.New(BasicHandler, func(ctx *fiber.Ctx) bool { + app.Use(skip.New(BasicHandler, func(ctx fiber.Ctx) bool { return ctx.Method() == fiber.MethodGet })) - app.Get("/", func(ctx *fiber.Ctx) error { + app.Get("/", func(ctx fiber.Ctx) error { return ctx.SendString("It was a GET request!") }) log.Fatal(app.Listen(":3000")) } -func BasicHandler(ctx *fiber.Ctx) error { +func BasicHandler(ctx fiber.Ctx) error { return ctx.SendString("It was not a GET request!") } ``` diff --git a/versioned_docs/version-v2.x/api/middleware/timeout.md b/versioned_docs/version-v2.x/api/middleware/timeout.md index 5fdf18d5275..059272fc210 100644 --- a/versioned_docs/version-v2.x/api/middleware/timeout.md +++ b/versioned_docs/version-v2.x/api/middleware/timeout.md @@ -36,8 +36,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/timeout" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/timeout" ) ``` @@ -46,8 +46,12 @@ After you initiate your Fiber app, you can use the following possibilities: ```go func main() { app := fiber.New() +<<<<<<< HEAD:middleware/timeout/README.md + h := func(c fiber.Ctx) error { +======= - h := func(c *fiber.Ctx) error { + h := func(c fiber.Ctx) error { +>>>>>>> origin/master:docs/api/middleware/timeout.md sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") if err := sleepWithContext(c.UserContext(), sleepTime); err != nil { return fmt.Errorf("%w: execution error", err) @@ -93,7 +97,7 @@ var ErrFooTimeOut = errors.New("foo context canceled") func main() { app := fiber.New() - h := func(c *fiber.Ctx) error { + h := func(c fiber.Ctx) error { sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") if err := sleepWithContextWithCustomError(c.UserContext(), sleepTime); err != nil { return fmt.Errorf("%w: execution error", err) @@ -126,7 +130,7 @@ func main() { app := fiber.New() db, _ := gorm.Open(postgres.Open("postgres://localhost/foodb"), &gorm.Config{}) - handler := func(ctx *fiber.Ctx) error { + handler := func(ctx fiber.Ctx) error { tran := db.WithContext(ctx.UserContext()).Begin() if tran = tran.Exec("SELECT pg_sleep(50)"); tran.Error != nil { diff --git a/versioned_docs/version-v2.x/extra/faq.md b/versioned_docs/version-v2.x/extra/faq.md index 54dbca5d15e..3a6062b00ef 100644 --- a/versioned_docs/version-v2.x/extra/faq.md +++ b/versioned_docs/version-v2.x/extra/faq.md @@ -25,7 +25,7 @@ If you're using v2.32.0 or later, all you need to do is to implement a custom er If you're using v2.31.0 or earlier, the error handler will not capture 404 errors. Instead, you need to add a middleware function at the very bottom of the stack \(below all other functions\) to handle a 404 response: ```go title="Example" -app.Use(func(c *fiber.Ctx) error { +app.Use(func(c fiber.Ctx) error { return c.Status(fiber.StatusNotFound).SendString("Sorry can't find that!") }) ``` @@ -66,7 +66,7 @@ To override the default error handler, you can override the default when providi ```go title="Example" app := fiber.New(fiber.Config{ - ErrorHandler: func(c *fiber.Ctx, err error) error { + ErrorHandler: func(c fiber.Ctx, err error) error { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) }, }) @@ -107,8 +107,8 @@ package main import ( "log" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" ) type Host struct { @@ -126,7 +126,7 @@ func main() { Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) hosts["api.localhost:3000"] = &Host{api} - api.Get("/", func(c *fiber.Ctx) error { + api.Get("/", func(c fiber.Ctx) error { return c.SendString("API") }) //------ @@ -137,7 +137,7 @@ func main() { Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) hosts["blog.localhost:3000"] = &Host{blog} - blog.Get("/", func(c *fiber.Ctx) error { + blog.Get("/", func(c fiber.Ctx) error { return c.SendString("Blog") }) //--------- @@ -149,12 +149,12 @@ func main() { })) hosts["localhost:3000"] = &Host{site} - site.Get("/", func(c *fiber.Ctx) error { + site.Get("/", func(c fiber.Ctx) error { return c.SendString("Website") }) // Server app := fiber.New() - app.Use(func(c *fiber.Ctx) error { + app.Use(func(c fiber.Ctx) error { host := hosts[c.Hostname()] if host == nil { return c.SendStatus(fiber.StatusNotFound) diff --git a/versioned_docs/version-v2.x/guide/error-handling.md b/versioned_docs/version-v2.x/guide/error-handling.md index 7d3aa361a9b..7817443b443 100644 --- a/versioned_docs/version-v2.x/guide/error-handling.md +++ b/versioned_docs/version-v2.x/guide/error-handling.md @@ -19,7 +19,7 @@ It’s essential to ensure that Fiber catches all errors that occur while runnin ```go -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // Pass error to Fiber return c.SendFile("file-does-not-exist") }) @@ -35,8 +35,8 @@ package main import ( "log" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/recover" ) func main() { @@ -44,7 +44,7 @@ func main() { app.Use(recover.New()) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { panic("This panic is caught by fiber") }) @@ -55,7 +55,7 @@ func main() { You could use Fiber's custom error struct to pass an additional `status code` using `fiber.NewError()`. It's optional to pass a message; if this is left empty, it will default to the status code message \(`404` equals `Not Found`\). ```go title="Example" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { // 503 Service Unavailable return fiber.ErrServiceUnavailable @@ -70,7 +70,7 @@ Fiber provides an error handler by default. For a standard error, the response i ```go title="Example" // Default error handler -var DefaultErrorHandler = func(c *fiber.Ctx, err error) error { +var DefaultErrorHandler = func(c fiber.Ctx, err error) error { // Status code defaults to 500 code := fiber.StatusInternalServerError @@ -100,7 +100,7 @@ The following example shows how to display error pages for different types of er // Create a new fiber instance with custom config app := fiber.New(fiber.Config{ // Override default error handler - ErrorHandler: func(ctx *fiber.Ctx, err error) error { + ErrorHandler: func(ctx fiber.Ctx, err error) error { // Status code defaults to 500 code := fiber.StatusInternalServerError diff --git a/versioned_docs/version-v2.x/guide/faster-fiber.md b/versioned_docs/version-v2.x/guide/faster-fiber.md index b0b7bcb3e5c..95b8ec96cb8 100644 --- a/versioned_docs/version-v2.x/guide/faster-fiber.md +++ b/versioned_docs/version-v2.x/guide/faster-fiber.md @@ -16,7 +16,7 @@ Since Fiber v2.32.0, we use **encoding/json** as default json library due to sta ```go title="Example" package main -import "github.com/gofiber/fiber/v2" +import "github.com/gofiber/fiber/v3" import "github.com/goccy/go-json" func main() { @@ -33,4 +33,4 @@ func main() { - [Set custom JSON encoder for client](../api/client.md#jsonencoder) - [Set custom JSON decoder for client](../api/client.md#jsondecoder) - [Set custom JSON encoder for application](../api/fiber.md#config) -- [Set custom JSON decoder for application](../api/fiber.md#config) \ No newline at end of file +- [Set custom JSON decoder for application](../api/fiber.md#config) diff --git a/versioned_docs/version-v2.x/guide/grouping.md b/versioned_docs/version-v2.x/guide/grouping.md index 429e170229c..4ed4622c184 100644 --- a/versioned_docs/version-v2.x/guide/grouping.md +++ b/versioned_docs/version-v2.x/guide/grouping.md @@ -62,12 +62,12 @@ Group handlers can also be used as a routing path but they must have **Next** ad func main() { app := fiber.New() - handler := func(c *fiber.Ctx) error { + handler := func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } api := app.Group("/api") // /api - v1 := api.Group("/v1", func(c *fiber.Ctx) error { // middleware for /api/v1 + v1 := api.Group("/v1", func(c fiber.Ctx) error { // middleware for /api/v1 c.Set("Version", "v1") return c.Next() }) diff --git a/versioned_docs/version-v2.x/guide/hooks.md b/versioned_docs/version-v2.x/guide/hooks.md index 357d59d33d2..9fd0cf4726b 100644 --- a/versioned_docs/version-v2.x/guide/hooks.md +++ b/versioned_docs/version-v2.x/guide/hooks.md @@ -35,7 +35,7 @@ type OnMountHandler = func(*App) error OnRoute is a hook to execute user functions on each route registeration. Also you can get route properties by **route** parameter. ```go title="Signature" -func (h *Hooks) OnRoute(handler ...OnRouteHandler) +func (app *App) OnRoute(handler ...OnRouteHandler) ``` ## OnName @@ -47,7 +47,7 @@ OnName only works with naming routes, not groups. ::: ```go title="Signature" -func (h *Hooks) OnName(handler ...OnNameHandler) +func (app *App) OnName(handler ...OnNameHandler) ``` @@ -59,13 +59,13 @@ package main import ( "fmt" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) func main() { app := fiber.New() - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { return c.SendString(c.Route().Name) }).Name("index") @@ -81,11 +81,11 @@ func main() { return nil }) - app.Get("/add/user", func(c *fiber.Ctx) error { + app.Get("/add/user", func(c fiber.Ctx) error { return c.SendString(c.Route().Name) }).Name("addUser") - app.Delete("/destroy/user", func(c *fiber.Ctx) error { + app.Delete("/destroy/user", func(c fiber.Ctx) error { return c.SendString(c.Route().Name) }).Name("destroyUser") @@ -104,7 +104,7 @@ func main() { OnGroup is a hook to execute user functions on each group registeration. Also you can get group properties by **group** parameter. ```go title="Signature" -func (h *Hooks) OnGroup(handler ...OnGroupHandler) +func (app *App) OnGroup(handler ...OnGroupHandler) ``` ## OnGroupName @@ -116,7 +116,7 @@ OnGroupName only works with naming groups, not routes. ::: ```go title="Signature" -func (h *Hooks) OnGroupName(handler ...OnGroupNameHandler) +func (app *App) OnGroupName(handler ...OnGroupNameHandler) ``` ## OnListen @@ -124,7 +124,7 @@ func (h *Hooks) OnGroupName(handler ...OnGroupNameHandler) OnListen is a hook to execute user functions on Listen, ListenTLS, Listener. ```go title="Signature" -func (h *Hooks) OnListen(handler ...OnListenHandler) +func (app *App) OnListen(handler ...OnListenHandler) ``` @@ -158,7 +158,7 @@ app.Listen(":5000") OnFork is a hook to execute user functions on Fork. ```go title="Signature" -func (h *Hooks) OnFork(handler ...OnForkHandler) +func (app *App) OnFork(handler ...OnForkHandler) ``` ## OnShutdown @@ -166,7 +166,7 @@ func (h *Hooks) OnFork(handler ...OnForkHandler) OnShutdown is a hook to execute user functions after Shutdown. ```go title="Signature" -func (h *Hooks) OnShutdown(handler ...OnShutdownHandler) +func (app *App) OnShutdown(handler ...OnShutdownHandler) ``` ## OnMount @@ -186,7 +186,7 @@ package main import ( "fmt" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) func main() { diff --git a/versioned_docs/version-v2.x/guide/routing.md b/versioned_docs/version-v2.x/guide/routing.md index 2a1067ae6f2..b8ab756694c 100644 --- a/versioned_docs/version-v2.x/guide/routing.md +++ b/versioned_docs/version-v2.x/guide/routing.md @@ -23,17 +23,17 @@ Route paths, combined with a request method, define the endpoints at which reque ```go // This route path will match requests to the root route, "/": -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("root") }) // This route path will match requests to "/about": -app.Get("/about", func(c *fiber.Ctx) error { +app.Get("/about", func(c fiber.Ctx) error { return c.SendString("about") }) // This route path will match requests to "/random.txt": -app.Get("/random.txt", func(c *fiber.Ctx) error { +app.Get("/random.txt", func(c fiber.Ctx) error { return c.SendString("random.txt") }) ``` @@ -59,28 +59,28 @@ The routing also offers the possibility to use optional parameters, for the name ```go // Parameters -app.Get("/user/:name/books/:title", func(c *fiber.Ctx) error { +app.Get("/user/:name/books/:title", func(c fiber.Ctx) error { fmt.Fprintf(c, "%s\n", c.Params("name")) fmt.Fprintf(c, "%s\n", c.Params("title")) return nil }) // Plus - greedy - not optional -app.Get("/user/+", func(c *fiber.Ctx) error { +app.Get("/user/+", func(c fiber.Ctx) error { return c.SendString(c.Params("+")) }) // Optional parameter -app.Get("/user/:name?", func(c *fiber.Ctx) error { +app.Get("/user/:name?", func(c fiber.Ctx) error { return c.SendString(c.Params("name")) }) // Wildcard - greedy - optional -app.Get("/user/*", func(c *fiber.Ctx) error { +app.Get("/user/*", func(c fiber.Ctx) error { return c.SendString(c.Params("*")) }) // This route path will match requests to "/v1/some/resource/name:customVerb", since the parameter character is escaped -app.Get(`/v1/some/resource/name\:customVerb`, func(c *fiber.Ctx) error { +app.Get(`/v1/some/resource/name\:customVerb`, func(c fiber.Ctx) error { return c.SendString("Hello, Community") }) ``` @@ -95,7 +95,7 @@ All special parameter characters can also be escaped with `"\\"` and lose their ```go // http://localhost:3000/plantae/prunus.persica -app.Get("/plantae/:genus.:species", func(c *fiber.Ctx) error { +app.Get("/plantae/:genus.:species", func(c fiber.Ctx) error { fmt.Fprintf(c, "%s.%s\n", c.Params("genus"), c.Params("species")) return nil // prunus.persica }) @@ -103,7 +103,7 @@ app.Get("/plantae/:genus.:species", func(c *fiber.Ctx) error { ```go // http://localhost:3000/flights/LAX-SFO -app.Get("/flights/:from-:to", func(c *fiber.Ctx) error { +app.Get("/flights/:from-:to", func(c fiber.Ctx) error { fmt.Fprintf(c, "%s-%s\n", c.Params("from"), c.Params("to")) return nil // LAX-SFO }) @@ -113,7 +113,7 @@ Our intelligent router recognizes that the introductory parameter characters sho ```go // http://localhost:3000/shop/product/color:blue/size:xs -app.Get("/shop/product/color::color/size::size", func(c *fiber.Ctx) error { +app.Get("/shop/product/color::color/size::size", func(c fiber.Ctx) error { fmt.Fprintf(c, "%s:%s\n", c.Params("color"), c.Params("size")) return nil // blue:xs }) @@ -170,7 +170,7 @@ Constraints aren't validation for parameters. If constraints aren't valid for a ```go -app.Get("/:test", func(c *fiber.Ctx) error { +app.Get("/:test", func(c fiber.Ctx) error { return c.SendString(c.Params("test")) }) @@ -185,7 +185,7 @@ app.Get("/:test", func(c *fiber.Ctx) error { You can use `;` for multiple constraints. ```go -app.Get("/:test", func(c *fiber.Ctx) error { +app.Get("/:test", func(c fiber.Ctx) error { return c.SendString(c.Params("test")) }) @@ -203,7 +203,7 @@ app.Get("/:test", func(c *fiber.Ctx) error { Fiber precompiles regex query when to register routes. So there're no performance overhead for regex constraint. ```go -app.Get(`/:date`, func(c *fiber.Ctx) error { +app.Get(`/:date`, func(c fiber.Ctx) error { return c.SendString(c.Params("date")) }) @@ -229,7 +229,7 @@ You should use `\\` before routing-specific characters when to use datetime cons You can impose constraints on optional parameters as well. ```go -app.Get("/:test?", func(c *fiber.Ctx) error { +app.Get("/:test?", func(c fiber.Ctx) error { return c.SendString(c.Params("test")) }) // curl -X GET http://localhost:3000/42 @@ -240,6 +240,59 @@ app.Get("/:test?", func(c *fiber.Ctx) error { // Cannot GET /7.0 ``` +**Custom Constraint Example** + +Custom constraints can be added to Fiber using the `app.RegisterCustomConstraint` method. Your constraints have to be compatible with the `CustomConstraint` interface. + +It is a good idea to add external constraints to your project once you want to add more specific rules to your routes. +For example, you can add a constraint to check if a parameter is a valid ULID. + +```go +// CustomConstraint is an interface for custom constraints +type CustomConstraint interface { + // Name returns the name of the constraint. + // This name is used in the constraint matching. + Name() string + + // Execute executes the constraint. + // It returns true if the constraint is matched and right. + // param is the parameter value to check. + // args are the constraint arguments. + Execute(param string, args ...string) bool +} +``` + +You can check the example below: + +```go +type UlidConstraint struct { + fiber.CustomConstraint +} + +func (*UlidConstraint) Name() string { + return "ulid" +} + +func (*UlidConstraint) Execute(param string, args ...string) bool { + _, err := ulid.Parse(param) + return err == nil +} + +func main() { + app := fiber.New() + app.RegisterCustomConstraint(&UlidConstraint{}) + + app.Get("/login/:id", func(c fiber.Ctx) error { + return c.SendString("...") + }) + + app.Listen(":3000") + + // /login/01HK7H9ZE5BFMK348CPYP14S0Z -> 200 + // /login/12345 -> 404 +} +``` + ## Middleware Functions that are designed to make changes to the request or response are called **middleware functions**. The [Next](../api/ctx.md#next) is a **Fiber** router function, when called, executes the **next** function that **matches** the current route. @@ -247,7 +300,7 @@ Functions that are designed to make changes to the request or response are calle **Example of a middleware function** ```go -app.Use(func(c *fiber.Ctx) error { +app.Use(func(c fiber.Ctx) error { // Set a custom header on all responses: c.Set("X-Custom-Header", "Hello, World") @@ -255,7 +308,7 @@ app.Use(func(c *fiber.Ctx) error { return c.Next() }) -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) ``` diff --git a/versioned_docs/version-v2.x/guide/templates.md b/versioned_docs/version-v2.x/guide/templates.md index cc52c2d9ac0..2bd441fe28b 100644 --- a/versioned_docs/version-v2.x/guide/templates.md +++ b/versioned_docs/version-v2.x/guide/templates.md @@ -18,7 +18,7 @@ Fiber provides a Views interface to provide your own template engine: ```go type Views interface { Load() error - Render(io.Writer, string, interface{}, ...string) error + Render(out io.Writer, name string, binding any, layout ...string) error } ``` @@ -39,7 +39,7 @@ The `Render` method is linked to the [**ctx.Render\(\)**](../api/ctx.md#render) If the Fiber config option `PassLocalsToViews` is enabled, then all locals set using `ctx.Locals(key, value)` will be passed to the template. ```go -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.Render("index", fiber.Map{ "hello": "world", }); @@ -68,7 +68,7 @@ package main import ( "log" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/gofiber/template/html/v2" ) @@ -82,7 +82,7 @@ func main() { app := fiber.New(fiber.Config{ Views: engine, }) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { // Render index template return c.Render("index", fiber.Map{ "Title": "Hello, World!", diff --git a/versioned_docs/version-v2.x/guide/validation.md b/versioned_docs/version-v2.x/guide/validation.md index 417298aebb0..58131bc0163 100644 --- a/versioned_docs/version-v2.x/guide/validation.md +++ b/versioned_docs/version-v2.x/guide/validation.md @@ -23,7 +23,7 @@ import ( "strings" "github.com/go-playground/validator/v10" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) type ( @@ -36,7 +36,7 @@ type ( Error bool FailedField string Tag string - Value interface{} + Value any } XValidator struct { @@ -53,7 +53,7 @@ type ( // for more information see: https://github.com/go-playground/validator var validate = validator.New() -func (v XValidator) Validate(data interface{}) []ErrorResponse { +func (v XValidator) Validate(data any) []ErrorResponse { validationErrors := []ErrorResponse{} errs := validate.Struct(data) @@ -81,7 +81,7 @@ func main() { app := fiber.New(fiber.Config{ // Global custom error handler - ErrorHandler: func(c *fiber.Ctx, err error) error { + ErrorHandler: func(c fiber.Ctx, err error) error { return c.Status(fiber.StatusBadRequest).JSON(GlobalErrorHandlerResp{ Success: false, Message: err.Error(), @@ -95,10 +95,10 @@ func main() { return fl.Field().Int() >= 12 && fl.Field().Int() <= 18 }) - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { user := &User{ - Name: c.Query("name"), - Age: c.QueryInt("age"), + Name: fiber.Query[string](c, "name"), + Age: fiber.Query[int](c, "age"), } // Validation diff --git a/versioned_docs/version-v2.x/intro.md b/versioned_docs/version-v2.x/intro.md index 456035da6bf..2c7eb8ef409 100644 --- a/versioned_docs/version-v2.x/intro.md +++ b/versioned_docs/version-v2.x/intro.md @@ -8,25 +8,25 @@ An online API documentation with examples so you can start building web apps wit **Fiber** is an [Express](https://github.com/expressjs/express) inspired **web framework** built on top of [Fasthttp](https://github.com/valyala/fasthttp), the **fastest** HTTP engine for [Go](https://go.dev/doc/). Designed to **ease** things up for **fast** development with **zero memory allocation** and **performance** in mind. -These docs are for **Fiber v2**, which was released on **September 15th, 2020**. +These docs are for **Fiber v3**, which was released on **March XX, 2024**. ### Installation -First of all, [download](https://go.dev/dl/) and install Go. `1.17` or higher is required. +First of all, [download](https://go.dev/dl/) and install Go. `1.21` or higher is required. Installation is done using the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: ```bash -go get github.com/gofiber/fiber/v2 +go get github.com/gofiber/fiber/v3 ``` ### Zero Allocation -Some values returned from \***fiber.Ctx** are **not** immutable by default. +Some values returned from **fiber.Ctx** are **not** immutable by default. Because fiber is optimized for **high-performance**, values returned from **fiber.Ctx** are **not** immutable by default and **will** be re-used across requests. As a rule of thumb, you **must** only use context values within the handler, and you **must not** keep any references. As soon as you return from the handler, any values you have obtained from the context will be re-used in future requests and will change below your feet. Here is an example: ```go -func handler(c *fiber.Ctx) error { +func handler(c fiber.Ctx) error { // Variable is only valid within this handler result := c.Params("foo") @@ -37,7 +37,7 @@ func handler(c *fiber.Ctx) error { If you need to persist such values outside the handler, make copies of their **underlying buffer** using the [copy](https://pkg.go.dev/builtin/#copy) builtin. Here is an example for persisting a string: ```go -func handler(c *fiber.Ctx) error { +func handler(c fiber.Ctx) error { // Variable is only valid within this handler result := c.Params("foo") @@ -54,7 +54,7 @@ func handler(c *fiber.Ctx) error { We created a custom `CopyString` function that does the above and is available under [gofiber/utils](https://github.com/gofiber/fiber/tree/master/utils). ```go -app.Get("/:foo", func(c *fiber.Ctx) error { +app.Get("/:foo", func(c fiber.Ctx) error { // Variable is now immutable result := utils.CopyString(c.Params("foo")) @@ -79,12 +79,12 @@ Embedded below is essentially the most straightforward **Fiber** app you can cre ```go package main -import "github.com/gofiber/fiber/v2" +import "github.com/gofiber/fiber/v3" func main() { app := fiber.New() - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) @@ -108,19 +108,19 @@ Route definition takes the following structures: ```go // Function signature -app.Method(path string, ...func(*fiber.Ctx) error) +app.Method(path string, ...func(fiber.Ctx) error) ``` - `app` is an instance of **Fiber** - `Method` is an [HTTP request method](https://docs.gofiber.io/api/app#route-handlers): `GET`, `PUT`, `POST`, etc. - `path` is a virtual path on the server -- `func(*fiber.Ctx) error` is a callback function containing the [Context](https://docs.gofiber.io/api/ctx) executed when the route is matched +- `func(fiber.Ctx) error` is a callback function containing the [Context](https://docs.gofiber.io/api/ctx) executed when the route is matched **Simple route** ```go // Respond with "Hello, World!" on root path, "/" -app.Get("/", func(c *fiber.Ctx) error { +app.Get("/", func(c fiber.Ctx) error { return c.SendString("Hello, World!") }) ``` @@ -130,7 +130,7 @@ app.Get("/", func(c *fiber.Ctx) error { ```go // GET http://localhost:8080/hello%20world -app.Get("/:value", func(c *fiber.Ctx) error { +app.Get("/:value", func(c fiber.Ctx) error { return c.SendString("value: " + c.Params("value")) // => Get request with value: hello world }) @@ -141,7 +141,7 @@ app.Get("/:value", func(c *fiber.Ctx) error { ```go // GET http://localhost:3000/john -app.Get("/:name?", func(c *fiber.Ctx) error { +app.Get("/:name?", func(c fiber.Ctx) error { if c.Params("name") != "" { return c.SendString("Hello " + c.Params("name")) // => Hello john @@ -155,7 +155,7 @@ app.Get("/:name?", func(c *fiber.Ctx) error { ```go // GET http://localhost:3000/api/user/john -app.Get("/api/*", func(c *fiber.Ctx) error { +app.Get("/api/*", func(c fiber.Ctx) error { return c.SendString("API path: " + c.Params("*")) // => API path: user/john }) diff --git a/versioned_docs/version-v2.x/partials/routing/handler.md b/versioned_docs/version-v2.x/partials/routing/handler.md index 2d198f02f8f..072cebe44d1 100644 --- a/versioned_docs/version-v2.x/partials/routing/handler.md +++ b/versioned_docs/version-v2.x/partials/routing/handler.md @@ -27,12 +27,12 @@ func (app *App) All(path string, handlers ...Handler) Router ```go title="Examples" // Simple GET handler -app.Get("/api/list", func(c *fiber.Ctx) error { +app.Get("/api/list", func(c fiber.Ctx) error { return c.SendString("I'm a GET request!") }) // Simple POST handler -app.Post("/api/register", func(c *fiber.Ctx) error { +app.Post("/api/register", func(c fiber.Ctx) error { return c.SendString("I'm a POST request!") }) ``` @@ -40,30 +40,30 @@ app.Post("/api/register", func(c *fiber.Ctx) error { **Use** can be used for middleware packages and prefix catchers. These routes will only match the beginning of each path i.e. `/john` will match `/john/doe`, `/johnnnnn` etc ```go title="Signature" -func (app *App) Use(args ...interface{}) Router +func (app *App) Use(args ...any) Router ``` ```go title="Examples" // Match any request -app.Use(func(c *fiber.Ctx) error { +app.Use(func(c fiber.Ctx) error { return c.Next() }) // Match request starting with /api -app.Use("/api", func(c *fiber.Ctx) error { +app.Use("/api", func(c fiber.Ctx) error { return c.Next() }) // Match requests starting with /api or /home (multiple-prefix support) -app.Use([]string{"/api", "/home"}, func(c *fiber.Ctx) error { +app.Use([]string{"/api", "/home"}, func(c fiber.Ctx) error { return c.Next() }) // Attach multiple handlers -app.Use("/api", func(c *fiber.Ctx) error { +app.Use("/api", func(c fiber.Ctx) error { c.Set("X-Custom-Header", random.String(32)) return c.Next() -}, func(c *fiber.Ctx) error { +}, func(c fiber.Ctx) error { return c.Next() }) ```