Merge branch 'main' into renanbastos93improve-error-handling-csrf-middle

pull/3021/head
Juan Calderon-Perez 2024-09-08 06:34:29 -04:00 committed by GitHub
commit 7075406ef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
186 changed files with 6502 additions and 3323 deletions

View File

@ -71,11 +71,11 @@ Community leaders will follow these Community Impact Guidelines in determining t
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
[https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are available at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations).

View File

@ -5,9 +5,11 @@ Before making any changes to this repository, we kindly request you to initiate
Please note: we have a [code of conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the `Fiber` project.
## Pull Requests or Commits
Titles always we must use prefix according to below:
> 🔥 Feature, ♻️ Refactor, 🩹 Fix, 🚨 Test, 📚 Doc, 🎨 Style
- 🔥 Feature: Add flow to add person
- ♻️ Refactor: Rename file X to Y
- 🩹 Fix: Improve flow
@ -17,7 +19,7 @@ Titles always we must use prefix according to below:
All pull requests that contain a feature or fix are mandatory to have unit tests. Your PR is only to be merged if you respect this flow.
# 👍 Contribute
## 👍 Contribute
If you want to say **thank you** and/or support the active development of `Fiber`:

60
.github/README.md vendored
View File

@ -1,4 +1,4 @@
<p align="center">
<h1 align="center">
<a href="https://gofiber.io">
<picture>
<source height="125" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/gofiber/docs/master/static/img/logo-dark.svg">
@ -12,7 +12,7 @@
<a href="https://goreportcard.com/report/github.com/gofiber/fiber/v3">
<img src="https://img.shields.io/badge/%F0%9F%93%9D%20goreport-A%2B-75C46B?style=flat-square">
</a>
<a href="https://codecov.io/gh/gofiber/fiber" >
<a href="https://codecov.io/gh/gofiber/fiber" >
<img alt="Codecov" src="https://img.shields.io/codecov/c/github/gofiber/fiber?token=3Cr92CwaPQ&style=flat-square&logo=codecov&label=codecov">
</a>
<a href="https://github.com/gofiber/fiber/actions?query=workflow%3ATest">
@ -24,7 +24,7 @@
<a href="https://gofiber.io/discord">
<img src="https://img.shields.io/discord/704680098577514527?style=flat-square&label=%F0%9F%92%AC%20discord&color=00ACD7">
</a>
</p>
</h1>
<p align="center">
<em><b>Fiber</b> is an <a href="https://github.com/expressjs/express">Express</a> inspired <b>web framework</b> built on top of <a href="https://github.com/valyala/fasthttp">Fasthttp</a>, the <b>fastest</b> HTTP engine for <a href="https://go.dev/doc/">Go</a>. Designed to <b>ease</b> things up for <b>fast</b> development with <a href="https://docs.gofiber.io/#zero-allocation"><b>zero memory allocation</b></a> and <b>performance</b> in mind.</em>
</p>
@ -39,7 +39,7 @@ Fiber v3 is currently in beta and under active development. While it offers exci
## ⚙️ Installation
Fiber requires **Go version `1.21` or higher** to run. If you need to install or upgrade Go, visit the [official Go download page](https://go.dev/dl/). To start setting up your project. Create a new directory for your project and navigate into it. Then, initialize your project with Go modules by executing the following command in your terminal:
Fiber requires **Go version `1.22` or higher** to run. If you need to install or upgrade Go, visit the [official Go download page](https://go.dev/dl/). To start setting up your project. Create a new directory for your project and navigate into it. Then, initialize your project with Go modules by executing the following command in your terminal:
```bash
go mod init github.com/your/repo
@ -57,7 +57,7 @@ This command fetches the Fiber package and adds it to your project's dependencie
## ⚡️ Quickstart
Getting started with Fiber is easy. Here's a basic example to create a simple web server that responds with "Hello, World 👋!" on the root path. This example demonstrates initializing a new Fiber app, setting up a route, and starting the server.
Getting started with Fiber is easy. Here's a basic example to create a simple web server that responds with "Hello, World 👋!" on the root path. This example demonstrates initializing a new Fiber app, setting up a route, and starting the server.
```go
package main
@ -100,19 +100,19 @@ These tests are performed by [TechEmpower](https://www.techempower.com/benchmark
## 🎯 Features
- Robust [Routing](https://docs.gofiber.io/guide/routing)
- Serve [Static Files](https://docs.gofiber.io/api/app#static)
- Extreme [Performance](https://docs.gofiber.io/extra/benchmarks)
- [Low Memory](https://docs.gofiber.io/extra/benchmarks) footprint
- [API Endpoints](https://docs.gofiber.io/api/ctx)
- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) support
- [Rapid](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server-side programming
- [Template Engines](https://github.com/gofiber/template)
- [WebSocket Support](https://github.com/gofiber/contrib/tree/main/websocket)
- [Socket.io Support](https://github.com/gofiber/contrib/tree/main/socketio)
- [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse)
- [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter)
- And much more, [explore Fiber](https://docs.gofiber.io/)
- Robust [Routing](https://docs.gofiber.io/guide/routing)
- Serve [Static Files](https://docs.gofiber.io/api/app#static)
- Extreme [Performance](https://docs.gofiber.io/extra/benchmarks)
- [Low Memory](https://docs.gofiber.io/extra/benchmarks) footprint
- [API Endpoints](https://docs.gofiber.io/api/ctx)
- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) support
- [Rapid](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server-side programming
- [Template Engines](https://github.com/gofiber/template)
- [WebSocket Support](https://github.com/gofiber/contrib/tree/main/websocket)
- [Socket.io Support](https://github.com/gofiber/contrib/tree/main/socketio)
- [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse)
- [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter)
- And much more, [explore Fiber](https://docs.gofiber.io/)
## 💡 Philosophy
@ -124,14 +124,14 @@ We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues),
## ⚠️ Limitations
- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber v3 has been tested with Go versions 1.21 and 1.22.
- Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber v3 has been tested with Go versions 1.22 and 1.23.
- Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem.
## 👀 Examples
Listed below are some of the common examples. If you want to see more code examples, please visit our [Recipes repository](https://github.com/gofiber/recipes) or visit our hosted [API documentation](https://docs.gofiber.io).
#### 📖 [**Basic Routing**](https://docs.gofiber.io/#basic-routing)
### 📖 [**Basic Routing**](https://docs.gofiber.io/#basic-routing)
```go
func main() {
@ -615,7 +615,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team](
| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- |
| [contrib](https://github.com/gofiber/contrib) | Third party middlewares |
| [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. |
| [template](https://github.com/gofiber/template) | This package contains 9 template engines that can be used with Fiber `v3` Go version 1.21 or higher is required. |
| [template](https://github.com/gofiber/template) | This package contains 9 template engines that can be used with Fiber `v3` Go version 1.22 or higher is required. |
## 🕶️ Awesome List
@ -634,14 +634,14 @@ If you want to say **Thank You** and/or support the active development of `Fiber
To ensure your contributions are ready for a Pull Request, please use the following `Makefile` commands. These tools help maintain code quality, consistency.
* **make help**: Display available commands.
* **make audit**: Conduct quality checks.
* **make benchmark**: Benchmark code performance.
* **make coverage**: Generate test coverage report.
* **make format**: Automatically format code.
* **make lint**: Run lint checks.
* **make test**: Execute all tests.
* **make tidy**: Tidy dependencies.
- **make help**: Display available commands.
- **make audit**: Conduct quality checks.
- **make benchmark**: Benchmark code performance.
- **make coverage**: Generate test coverage report.
- **make format**: Automatically format code.
- **make lint**: Run lint checks.
- **make test**: Execute all tests.
- **make tidy**: Tidy dependencies.
Run these commands to ensure your code adheres to project standards and best practices.

6
.github/SECURITY.md vendored
View File

@ -6,6 +6,7 @@
4. [Incident Response Process](#process)
<a name="versions"></a>
## Supported Versions
The table below shows the supported versions for Fiber which include security updates.
@ -16,6 +17,7 @@ The table below shows the supported versions for Fiber which include security up
| < 1.12.6 | :x: |
<a name="reporting"></a>
## Reporting security problems to Fiber
**DO NOT CREATE AN ISSUE** to report a security problem. Instead, please
@ -24,6 +26,7 @@ send us an e-mail at `team@gofiber.io` or join our discord server via
to Fenny or any of the maintainers.
<a name="contact"></a>
## Security Point of Contact
The security point of contact is [Fenny](https://github.com/Fenny). Fenny responds
@ -35,6 +38,7 @@ of contact are any of the [@maintainers](https://github.com/orgs/gofiber/teams/m
The maintainers are the only other persons with administrative access to Fiber's source code.
<a name="process"></a>
## Incident Response Process
In case an incident is discovered or reported, we will follow the following
@ -73,4 +77,4 @@ for all of it's members.
We learn about critical software updates and security threats from these sources
1. GitHub Security Alerts
2. GitHub: https://status.github.com/ & [@githubstatus](https://twitter.com/githubstatus)
2. GitHub: [https://status.github.com/](https://status.github.com/) & [@githubstatus](https://twitter.com/githubstatus)

View File

@ -1,4 +1,4 @@
## Description
# Description
Please provide a clear and concise description of the changes you've made and the problem they address. Include the purpose of the change, any relevant issues it solves, and the benefits it brings to the project. If this change introduces new features or adjustments, highlight them here.

View File

@ -5,6 +5,7 @@ on:
types: [opened, edited, milestoned]
pull_request_target:
types: [opened]
permissions:
contents: read
issues: write

View File

@ -3,15 +3,19 @@ on:
branches:
- master
- main
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
pull_request:
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
permissions:
# deployments permission to deploy GitHub pages website
deployments: write
# contents permission to update benchmark contents in gh-pages branch
contents: write
# allow posting comments to pull request
pull-requests: write
name: Benchmark
jobs:
@ -20,34 +24,88 @@ jobs:
steps:
- name: Fetch Repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # to be able to retrieve the last commit in main
- name: Install Go
uses: actions/setup-go@v5
with:
# NOTE: Keep this in sync with the version from go.mod
go-version: "1.21.x"
go-version: "1.22.x"
- name: Run Benchmark
run: set -o pipefail; go test ./... -benchmem -run=^$ -bench . | tee output.txt
- name: Get Previous Benchmark Results
uses: actions/cache@v4
# NOTE: Benchmarks could change with different CPU types
- name: Get GitHub Runner System Information
uses: kenchan0130/actions-system-info@v1.3.0
id: system-info
- name: Get Main branch SHA
id: get-main-branch-sha
run: |
SHA=$(git rev-parse origin/main)
echo "sha=$SHA" >> $GITHUB_OUTPUT
- name: Get Benchmark Results from main branch
id: cache
uses: actions/cache/restore@v4
with:
path: ./cache
key: ${{ runner.os }}-benchmark
key: ${{ steps.get-main-branch-sha.outputs.sha }}-${{ runner.os }}-${{ steps.system-info.outputs.cpu-model }}-benchmark
- name: Save Benchmark Results
# This will only run if we have Benchmark Results from main branch
- name: Compare PR Benchmark Results with main branch
uses: benchmark-action/github-action-benchmark@v1.20.3
if: steps.cache.outputs.cache-hit == 'true'
with:
tool: "go"
tool: 'go'
output-file-path: output.txt
github-token: ${{ secrets.BENCHMARK_TOKEN }}
benchmark-data-dir-path: "benchmarks"
external-data-json-path: ./cache/benchmark-data.json
# Do not save the data (This allows comparing benchmarks)
save-data-file: false
fail-on-alert: true
comment-on-alert: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
# Enable Job Summary for PRs - deactivated because of issues
#summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }}
# Comment on the PR if the branch is not a fork
comment-on-alert: ${{ github.event.pull_request.head.repo.fork == false }}
github-token: ${{ secrets.GITHUB_TOKEN }}
summary-always: true
alert-threshold: "150%"
- name: Store Benchmark Results for main branch
uses: benchmark-action/github-action-benchmark@v1.20.3
if: ${{ github.ref_name == 'main' }}
with:
tool: 'go'
output-file-path: output.txt
external-data-json-path: ./cache/benchmark-data.json
# Save the data to external file (cache)
save-data-file: true
fail-on-alert: false
github-token: ${{ secrets.GITHUB_TOKEN }}
summary-always: true
alert-threshold: "150%"
- name: Publish Benchmark Results to GitHub Pages
uses: benchmark-action/github-action-benchmark@v1.20.3
if: ${{ github.ref_name == 'main' }}
with:
tool: 'go'
output-file-path: output.txt
benchmark-data-dir-path: "benchmarks"
fail-on-alert: false
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-on-alert: true
summary-always: true
# Save the data to external file (GitHub Pages)
save-data-file: true
alert-threshold: "150%"
# TODO: reactivate it later -> when v3 is the stable one
#auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
auto-push: false
save-data-file: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
- name: Update Benchmark Results cache
uses: actions/cache/save@v4
if: ${{ github.ref_name == 'main' }}
with:
path: ./cache
key: ${{ steps.get-main-branch-sha.outputs.sha }}-${{ runner.os }}-${{ steps.system-info.outputs.cpu-model }}-benchmark

View File

@ -5,15 +5,11 @@ on:
branches:
- master
- main
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
pull_request:
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
schedule:
- cron: "0 3 * * 6"

View File

@ -6,7 +6,11 @@ on:
branches:
- master
- main
paths-ignore:
- "**/*.md"
pull_request:
paths-ignore:
- "**/*.md"
permissions:
# Required: allow read access to the content for analysis.
@ -26,11 +30,11 @@ jobs:
- uses: actions/setup-go@v5
with:
# NOTE: Keep this in sync with the version from go.mod
go-version: "1.21.x"
go-version: "1.22.x"
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
# NOTE: Keep this in sync with the version from .golangci.yml
version: v1.59.1
version: v1.60.3

46
.github/workflows/manual-dependabot.yml vendored Normal file
View File

@ -0,0 +1,46 @@
# https://github.com/dependabot/dependabot-script/blob/main/manual-github-actions.yaml
# https://github.com/dependabot/dependabot-script?tab=readme-ov-file#github-actions-standalone
name: ManualDependabot
on:
workflow_dispatch:
inputs:
package-manager:
description: 'The package manager to use'
required: true
default: 'gomod'
directory:
description: 'The directory to scan'
required: true
default: '/'
permissions:
contents: read
jobs:
dependabot:
permissions:
contents: write # for Git to git push
pull-requests: write # for repo-sync/pull-request to create pull requests
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Checkout dependabot
run: |
cd /tmp/
git clone https://github.com/dependabot/dependabot-script
- name: Build image
run: |
cd /tmp/dependabot-script
docker build -t "dependabot/dependabot-script" -f Dockerfile .
- name: Run dependabot
env:
PACKAGE_MANAGER: ${{ github.event.inputs.package-manager }}
DIRECTORY: ${{ github.event.inputs.directory }}
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
docker run -v $PWD:/src -e PROJECT_PATH=$GITHUB_REPOSITORY -e PACKAGE_MANAGER=$PACKAGE_MANAGER -e DIRECTORY=$DIRECTORY -e GITHUB_ACCESS_TOKEN=$GITHUB_ACCESS_TOKEN -e OPTIONS="$OPTIONS" dependabot/dependabot-script

22
.github/workflows/markdown.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: markdownlint
on:
push:
branches:
- master
- main
pull_request:
jobs:
markdownlint:
runs-on: ubuntu-latest
steps:
- name: Fetch Repository
uses: actions/checkout@v4
- name: Run markdownlint-cli2
uses: DavidAnson/markdownlint-cli2-action@v16
with:
globs: |
**/*.md
#vendor

View File

@ -5,22 +5,18 @@ on:
branches:
- master
- main
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
pull_request:
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
jobs:
unit:
strategy:
matrix:
go-version: [1.21.x, 1.22.x]
platform: [ubuntu-latest, windows-latest, macos-latest, macos-14]
go-version: [1.22.x, 1.23.x]
platform: [ubuntu-latest, windows-latest, macos-latest, macos-13]
runs-on: ${{ matrix.platform }}
steps:
- name: Fetch Repository
@ -35,7 +31,7 @@ jobs:
run: go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=1 -coverprofile=coverage.txt -covermode=atomic -shuffle=on
- name: Upload coverage reports to Codecov
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.22.x' }}
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.23.x' }}
uses: codecov/codecov-action@v4.5.0
with:
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -5,15 +5,11 @@ on:
branches:
- master
- main
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
pull_request:
paths:
- "**"
- "!docs/**"
- "!**.md"
paths-ignore:
- "**/*.md"
jobs:
govulncheck-check:

View File

@ -101,7 +101,6 @@ linters-settings:
govet:
enable-all: true
disable:
- fieldalignment
- shadow
grouper:
@ -279,7 +278,7 @@ linters:
- depguard
- dogsled
# - dupl
# - dupword # TODO: Enable
- dupword # TODO: Enable
- durationcheck
- errcheck
- errchkjson
@ -288,7 +287,7 @@ linters:
- exhaustive
# - exhaustivestruct
# - exhaustruct
- exportloopref
- copyloopvar
- forbidigo
- forcetypeassert
# - funlen
@ -299,7 +298,7 @@ linters:
# - gochecknoinits # TODO: Enable
- gochecksumtype
# - gocognit
# - goconst # TODO: Enable
- goconst # TODO: Enable
- gocritic
# - gocyclo
# - godot
@ -308,9 +307,8 @@ linters:
- gofmt
- gofumpt
# - goheader
# - goimports
# - golint
# - gomnd # TODO: Enable
- goimports
# - mnd # TODO: Enable
- gomoddirectives
# - gomodguard
- goprintffuncname

248
.markdownlint.yml Normal file
View File

@ -0,0 +1,248 @@
# Example markdownlint configuration with all properties set to their default value
# Default state for all rules
default: true
# Path to configuration file to extend
extends: null
# MD001/heading-increment : Heading levels should only increment by one level at a time : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md001.md
MD001: true
# MD003/heading-style : Heading style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md003.md
MD003:
# Heading style
style: "consistent"
# MD004/ul-style : Unordered list style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md004.md
MD004:
# List style
style: "consistent"
# MD005/list-indent : Inconsistent indentation for list items at the same level : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md005.md
MD005: true
# MD007/ul-indent : Unordered list indentation : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md007.md
MD007:
# Spaces for indent
indent:
# Whether to indent the first level of the list
start_indented: false
# Spaces for first level indent (when start_indented is set)
start_indent: 2
# MD009/no-trailing-spaces : Trailing spaces : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md009.md
MD009:
# Spaces for line break
br_spaces: 2
# Allow spaces for empty lines in list items
list_item_empty_lines: false
# Include unnecessary breaks
strict: true
# MD010/no-hard-tabs : Hard tabs : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md010.md
MD010:
# Include code blocks
code_blocks: true
# Fenced code languages to ignore
ignore_code_languages: []
# Number of spaces for each hard tab
spaces_per_tab: 4
# MD011/no-reversed-links : Reversed link syntax : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md011.md
MD011: true
# MD012/no-multiple-blanks : Multiple consecutive blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md012.md
MD012:
# Consecutive blank lines
maximum: 1
# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md
MD013: false
# MD014/commands-show-output : Dollar signs used before commands without showing output : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md014.md
MD014: true
# MD018/no-missing-space-atx : No space after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md018.md
MD018: true
# MD019/no-multiple-space-atx : Multiple spaces after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md019.md
MD019: true
# MD020/no-missing-space-closed-atx : No space inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md020.md
MD020: true
# MD021/no-multiple-space-closed-atx : Multiple spaces inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md021.md
MD021: true
# MD022/blanks-around-headings : Headings should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md
MD022:
# Blank lines above heading
lines_above: 1
# Blank lines below heading
lines_below: 1
# MD023/heading-start-left : Headings must start at the beginning of the line : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md023.md
MD023: true
# MD024/no-duplicate-heading : Multiple headings with the same content : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md024.md
MD024: false
# MD025/single-title/single-h1 : Multiple top-level headings in the same document : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md025.md
MD025:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD026/no-trailing-punctuation : Trailing punctuation in heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md026.md
MD026:
# Punctuation characters
punctuation: ".,;:!。,;:!"
# MD027/no-multiple-space-blockquote : Multiple spaces after blockquote symbol : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md027.md
MD027: true
# MD028/no-blanks-blockquote : Blank line inside blockquote : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md028.md
MD028: true
# MD029/ol-prefix : Ordered list item prefix : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md029.md
MD029:
# List style
style: "one_or_ordered"
# MD030/list-marker-space : Spaces after list markers : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md030.md
MD030:
# Spaces for single-line unordered list items
ul_single: 1
# Spaces for single-line ordered list items
ol_single: 1
# Spaces for multi-line unordered list items
ul_multi: 1
# Spaces for multi-line ordered list items
ol_multi: 1
# MD031/blanks-around-fences : Fenced code blocks should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md031.md
MD031:
# Include list items
list_items: true
# MD032/blanks-around-lists : Lists should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md032.md
MD032: true
# MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md033.md
MD033: false
# MD034/no-bare-urls : Bare URL used : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md034.md
MD034: true
# MD035/hr-style : Horizontal rule style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md035.md
MD035:
# Horizontal rule style
style: "consistent"
# MD036/no-emphasis-as-heading : Emphasis used instead of a heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md036.md
MD036:
# Punctuation characters
punctuation: ".,;:!?。,;:!?"
# MD037/no-space-in-emphasis : Spaces inside emphasis markers : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md037.md
MD037: true
# MD038/no-space-in-code : Spaces inside code span elements : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md038.md
MD038: true
# MD039/no-space-in-links : Spaces inside link text : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md039.md
MD039: true
# MD040/fenced-code-language : Fenced code blocks should have a language specified : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md040.md
MD040:
# List of languages
allowed_languages: []
# Require language only
language_only: false
# MD041/first-line-heading/first-line-h1 : First line in a file should be a top-level heading : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md041.md
MD041:
# Heading level
level: 1
# RegExp for matching title in front matter
front_matter_title: "^\\s*title\\s*[:=]"
# MD042/no-empty-links : No empty links : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md042.md
MD042: true
# MD043/required-headings : Required heading structure : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md043.md
MD043: false
# MD044/proper-names : Proper names should have the correct capitalization : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md044.md
MD044:
# List of proper names
names: []
# Include code blocks
code_blocks: true
# Include HTML elements
html_elements: true
# MD045/no-alt-text : Images should have alternate text (alt text) : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md045.md
MD045: false
# MD046/code-block-style : Code block style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md046.md
MD046:
# Block style
style: "fenced"
# MD047/single-trailing-newline : Files should end with a single newline character : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md047.md
MD047: true
# MD048/code-fence-style : Code fence style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md048.md
MD048:
# Code fence style
style: "backtick"
# MD049/emphasis-style : Emphasis style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md049.md
MD049:
# Emphasis style
style: "consistent"
# MD050/strong-style : Strong style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md050.md
MD050:
# Strong style
style: "consistent"
# MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md051.md
MD051: true
# MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md052.md
MD052:
# Include shortcut syntax
shortcut_syntax: false
# MD053/link-image-reference-definitions : Link and image reference definitions should be needed : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md053.md
MD053:
# Ignored definitions
ignored_definitions:
- "//"
# MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md054.md
MD054:
# Allow autolinks
autolink: false
# Allow inline links and images
inline: true
# Allow full reference links and images
full: true
# Allow collapsed reference links and images
collapsed: true
# Allow shortcut reference links and images
shortcut: true
# Allow URLs as inline links
url_inline: true
# MD055/table-pipe-style : Table pipe style : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md055.md
MD055:
# Table pipe style
style: "consistent"
# MD056/table-column-count : Table column count : https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md056.md
MD056: true

View File

@ -27,10 +27,15 @@ coverage:
format:
go run mvdan.cc/gofumpt@latest -w -l .
## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli)
.PHONY: markdown
markdown:
markdownlint-cli2 "**/*.md" "#vendor"
## lint: 🚨 Run lint checks
.PHONY: lint
lint:
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1 run ./...
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3 run ./...
## test: 🚦 Execute all tests
.PHONY: test
@ -46,3 +51,15 @@ longtest:
.PHONY: tidy
tidy:
go mod tidy -v
## betteralign: 📐 Optimize alignment of fields in structs
.PHONY: betteralign
betteralign:
go run github.com/dkorunic/betteralign/cmd/betteralign@latest -test_files -generated_files -apply ./...
## generate: ⚡️ Generate msgp && interface implementations
.PHONY: generate
generate:
go install github.com/tinylib/msgp@latest
go install github.com/vburenin/ifacemaker@975a95966976eeb2d4365a7fb236e274c54da64c
go generate ./...

View File

@ -1,20 +1,20 @@
# Retry Addon
Retry addon for [Fiber](https://github.com/gofiber/fiber) designed to apply retry mechanism for unsuccessful network
operations. This addon uses exponential backoff algorithm with jitter. It calls the function multiple times and tries
to make it successful. If all calls are failed, then, it returns error. It adds a jitter at each retry step because adding
a jitter is a way to break synchronization across the client and avoid collision.
operations. This addon uses an exponential backoff algorithm with jitter. It calls the function multiple times and tries
to make it successful. If all calls are failed, then, it returns an error. It adds a jitter at each retry step because adding
a jitter is a way to break synchronization across the client and avoid collision.
## Table of Contents
- [Retry Addon](#retry-addon)
- [Table of Contents](#table-of-contents)
- [Signatures](#signatures)
- [Examples](#examples)
- [Default Config](#default-config)
- [Custom Config](#custom-config)
- [Config](#config)
- [Default Config Example](#default-config-example)
- [Table of Contents](#table-of-contents)
- [Signatures](#signatures)
- [Examples](#examples)
- [Default Config](#default-config)
- [Custom Config](#custom-config)
- [Config](#config)
- [Default Config Example](#default-config-example)
## Signatures
@ -42,10 +42,10 @@ retry.NewExponentialBackoff()
```go
retry.NewExponentialBackoff(retry.Config{
InitialInterval: 2 * time.Second,
MaxBackoffTime: 64 * time.Second,
Multiplier: 2.0,
MaxRetryCount: 15,
InitialInterval: 2 * time.Second,
MaxBackoffTime: 64 * time.Second,
Multiplier: 2.0,
MaxRetryCount: 15,
})
```
@ -88,10 +88,10 @@ type Config struct {
```go
// DefaultConfig is the default config for retry.
var DefaultConfig = Config{
InitialInterval: 1 * time.Second,
MaxBackoffTime: 32 * time.Second,
Multiplier: 2.0,
MaxRetryCount: 10,
currentInterval: 1 * time.Second,
InitialInterval: 1 * time.Second,
MaxBackoffTime: 32 * time.Second,
Multiplier: 2.0,
MaxRetryCount: 10,
currentInterval: 1 * time.Second,
}
```
```

View File

@ -11,10 +11,10 @@ import (
func Test_ExponentialBackoff_Retry(t *testing.T) {
t.Parallel()
tests := []struct {
name string
expErr error
expBackoff *ExponentialBackoff
f func() error
expErr error
name string
}{
{
name: "With default values - successful",
@ -51,7 +51,6 @@ func Test_ExponentialBackoff_Retry(t *testing.T) {
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
err := tt.expBackoff.Retry(tt.f)
@ -106,7 +105,6 @@ func Test_ExponentialBackoff_Next(t *testing.T) {
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
for i := 0; i < tt.expBackoff.MaxRetryCount; i++ {

38
app.go
View File

@ -80,29 +80,16 @@ type ErrorHandler = func(Ctx, error) error
// Error represents an error that occurred while handling a request.
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
Code int `json:"code"`
}
// App denotes the Fiber application.
type App struct {
mutex sync.Mutex
// Route stack divided by HTTP methods
stack [][]*Route
// Route stack divided by HTTP methods and route prefixes
treeStack []map[string][]*Route
// contains the information if the route stack has been changed to build the optimized tree
routesRefreshed bool
// Amount of registered routes
routesCount uint32
// Amount of registered handlers
handlersCount uint32
// Ctx pool
pool sync.Pool
// Fasthttp server
server *fasthttp.Server
// App config
config Config
// Converts string to a byte slice
getBytes func(s string) (b []byte)
// Converts byte slice to a string
@ -113,24 +100,37 @@ type App struct {
latestRoute *Route
// newCtxFunc
newCtxFunc func(app *App) CustomCtx
// custom binders
customBinders []CustomBinder
// TLS handler
tlsHandler *TLSHandler
// Mount fields
mountFields *mountFields
// Indicates if the value was explicitly configured
configured Config
// Route stack divided by HTTP methods
stack [][]*Route
// Route stack divided by HTTP methods and route prefixes
treeStack []map[string][]*Route
// custom binders
customBinders []CustomBinder
// customConstraints is a list of external constraints
customConstraints []CustomConstraint
// sendfiles stores configurations for handling ctx.SendFile operations
sendfiles []*sendFileStore
// App config
config Config
// Indicates if the value was explicitly configured
configured Config
// sendfilesMutex is a mutex used for sendfile operations
sendfilesMutex sync.RWMutex
mutex sync.Mutex
// Amount of registered routes
routesCount uint32
// Amount of registered handlers
handlersCount uint32
// contains the information if the route stack has been changed to build the optimized tree
routesRefreshed bool
}
// Config is a struct holding the server settings.
type Config struct {
type Config struct { //nolint:govet // Aligning the struct fields is not necessary. betteralign:ignore
// Enables the "Server: value" HTTP header.
//
// Default: ""

View File

@ -26,9 +26,9 @@ func Test_Bind_Query(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -53,14 +53,14 @@ func Test_Bind_Query(t *testing.T) {
require.Empty(t, empty.Hobby)
type Query2 struct {
Bool bool
ID int
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1")
@ -237,8 +237,8 @@ func Test_Bind_Query_Schema(t *testing.T) {
require.Equal(t, "nested.age is empty", c.Bind().Query(q2).Error())
type Node struct {
Value int `query:"val,required"`
Next *Node `query:"next,required"`
Value int `query:"val,required"`
}
c.Request().URI().SetQueryString("val=1&next.val=3")
n := new(Node)
@ -292,9 +292,9 @@ func Test_Bind_Header(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Header struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -318,14 +318,14 @@ func Test_Bind_Header(t *testing.T) {
require.Empty(t, empty.Hobby)
type Header2 struct {
Bool bool
ID int
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Request().Header.Add("id", "2")
@ -502,8 +502,8 @@ func Test_Bind_Header_Schema(t *testing.T) {
require.Equal(t, "Nested.age is empty", c.Bind().Header(h2).Error())
type Node struct {
Value int `header:"Val,required"`
Next *Node `header:"Next,required"`
Value int `header:"Val,required"`
}
c.Request().Header.Add("Val", "1")
c.Request().Header.Add("Next.Val", "3")
@ -533,9 +533,9 @@ func Test_Bind_RespHeader(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Header struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -559,14 +559,14 @@ func Test_Bind_RespHeader(t *testing.T) {
require.Empty(t, empty.Hobby)
type Header2 struct {
Bool bool
ID int
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Response().Header.Add("id", "2")
@ -635,9 +635,9 @@ func Benchmark_Bind_Query(b *testing.B) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -708,9 +708,9 @@ func Benchmark_Bind_Query_Comma(b *testing.B) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -732,9 +732,9 @@ func Benchmark_Bind_Header(b *testing.B) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type ReqHeader struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -782,9 +782,9 @@ func Benchmark_Bind_RespHeader(b *testing.B) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type ReqHeader struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -1252,9 +1252,9 @@ func Test_Bind_Cookie(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Cookie struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
@ -1278,14 +1278,14 @@ func Test_Bind_Cookie(t *testing.T) {
require.Empty(t, empty.Hobby)
type Cookie2 struct {
Bool bool
ID int
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Request().Header.SetCookie("id", "2")
@ -1463,8 +1463,8 @@ func Test_Bind_Cookie_Schema(t *testing.T) {
require.Equal(t, "Nested.Age is empty", c.Bind().Cookie(h2).Error())
type Node struct {
Value int `cookie:"Val,required"`
Next *Node `cookie:"Next,required"`
Value int `cookie:"Val,required"`
}
c.Request().Header.SetCookie("Val", "1")
c.Request().Header.SetCookie("Next.Val", "3")
@ -1495,9 +1495,9 @@ func Benchmark_Bind_Cookie(b *testing.B) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Cookie struct {
ID int
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")

View File

@ -1,6 +1,7 @@
# Fiber Binders
Binder is new request/response binding feature for Fiber. By aganist old Fiber parsers, it supports custom binder registration, struct validation, **map[string]string**, **map[string][]string** and more. It's introduced in Fiber v3 and a replacement of:
Binder is a new request/response binding feature for Fiber. Against the old Fiber parsers, it supports custom binder registration, struct validation, `map[string]string`, `map[string][]string`, and more. It's introduced in Fiber v3 and a replacement of:
- BodyParser
- ParamsParser
- GetReqHeaders
@ -9,8 +10,8 @@ Binder is new request/response binding feature for Fiber. By aganist old Fiber p
- QueryParser
- ReqHeaderParser
## Default Binders
- [Form](form.go)
- [Query](query.go)
- [URI](uri.go)
@ -23,7 +24,9 @@ Binder is new request/response binding feature for Fiber. By aganist old Fiber p
## Guides
### Binding into the Struct
Fiber supports binding into the struct with [gorilla/schema](https://github.com/gorilla/schema). Here's an example for it:
Fiber supports binding into the struct with [gorilla/schema](https://github.com/gorilla/schema). Here's an example:
```go
// Field names should start with an uppercase letter
type Person struct {
@ -32,16 +35,16 @@ type Person struct {
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
p := new(Person)
if err := c.Bind().Body(p); err != nil {
return err
}
if err := c.Bind().Body(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
// ...
})
// Run tests with the following curl commands:
@ -58,29 +61,34 @@ app.Post("/", func(c fiber.Ctx) error {
```
### Binding into the Map
Fiber supports binding into the **map[string]string** or **map[string][]string**. Here's an example for it:
Fiber supports binding into the `map[string]string` or `map[string][]string`. Here's an example:
```go
app.Get("/", func(c fiber.Ctx) error {
p := make(map[string][]string)
p := make(map[string][]string)
if err := c.Bind().Query(p); err != nil {
return err
}
if err := c.Bind().Query(p); err != nil {
return err
}
log.Println(p["name"]) // john
log.Println(p["pass"]) // doe
log.Println(p["products"]) // [shoe, hat]
log.Println(p["name"]) // john
log.Println(p["pass"]) // doe
log.Println(p["products"]) // [shoe, hat]
// ...
// ...
})
// Run tests with the following curl command:
// curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat"
```
### Behaviors of Should/Must
Normally, Fiber returns binder error directly. However; if you want to handle it automatically, you can prefer `Must()`.
Normally, Fiber returns binder error directly. However; if you want to handle it automatically, you can prefer `Must()`.
If there's an error it'll return error and 400 as HTTP status. Here's an example for it:
```go
// Field names should start with an uppercase letter
type Person struct {
@ -89,106 +97,112 @@ type Person struct {
}
app.Get("/", func(c fiber.Ctx) error {
p := new(Person)
p := new(Person)
if err := c.Bind().Must().JSON(p); err != nil {
return err
// Status code: 400
// Response: Bad request: name is empty
}
if err := c.Bind().Must().JSON(p); err != nil {
return err
// Status code: 400
// Response: Bad request: name is empty
}
// ...
// ...
})
// Run tests with the following curl command:
// curl -X GET -H "Content-Type: application/json" --data "{\"pass\":\"doe\"}" localhost:3000
```
### Defining Custom Binder
We didn't add much binder to make Fiber codebase minimal. But if you want to use your binders, it's easy to register and use them. Here's an example for TOML binder.
We didn't add much binder to make Fiber codebase minimal. If you want to use your own binders, it's easy to register and use them. Here's an example for TOML binder.
```go
type Person struct {
Name string `toml:"name"`
Pass string `toml:"pass"`
Name string `toml:"name"`
Pass string `toml:"pass"`
}
type tomlBinding struct{}
func (b *tomlBinding) Name() string {
return "toml"
return "toml"
}
func (b *tomlBinding) MIMETypes() []string {
return []string{"application/toml"}
return []string{"application/toml"}
}
func (b *tomlBinding) Parse(c fiber.Ctx, out any) error {
return toml.Unmarshal(c.Body(), out)
return toml.Unmarshal(c.Body(), out)
}
func main() {
app := fiber.New()
app.RegisterCustomBinder(&tomlBinding{})
app := fiber.New()
app.RegisterCustomBinder(&tomlBinding{})
app.Get("/", func(c fiber.Ctx) error {
out := new(Person)
if err := c.Bind().Body(out); err != nil {
return err
}
app.Get("/", func(c fiber.Ctx) error {
out := new(Person)
if err := c.Bind().Body(out); err != nil {
return err
}
// or you can use like:
// if err := c.Bind().Custom("toml", out); err != nil {
// return err
// }
// or you can use like:
// if err := c.Bind().Custom("toml", out); err != nil {
// return err
// }
return c.SendString(out.Pass) // test
})
return c.SendString(out.Pass) // test
})
app.Listen(":3000")
app.Listen(":3000")
}
// curl -X GET -H "Content-Type: application/toml" --data "name = 'bar'
// pass = 'test'" localhost:3000
```
### Defining Custom Validator
All Fiber binders supporting struct validation if you defined validator inside of the config. You can create own validator, or use [go-playground/validator](https://github.com/go-playground/validator), [go-ozzo/ozzo-validation](https://github.com/go-ozzo/ozzo-validation)... Here's an example of simple custom validator:
```go
type Query struct {
Name string `query:"name"`
Name string `query:"name"`
}
type structValidator struct{}
func (v *structValidator) Engine() any {
return ""
return ""
}
func (v *structValidator) ValidateStruct(out any) error {
out = reflect.ValueOf(out).Elem().Interface()
sq := out.(Query)
out = reflect.ValueOf(out).Elem().Interface()
sq := out.(Query)
if sq.Name != "john" {
return errors.New("you should have entered right name!")
}
if sq.Name != "john" {
return errors.New("you should have entered right name!")
}
return nil
return nil
}
func main() {
app := fiber.New(fiber.Config{StructValidator: &structValidator{}})
app := fiber.New(fiber.Config{StructValidator: &structValidator{}})
app.Get("/", func(c fiber.Ctx) error {
out := new(Query)
if err := c.Bind().Query(out); err != nil {
return err // you should have entered right name!
}
return c.SendString(out.Name)
})
app.Get("/", func(c fiber.Ctx) error {
out := new(Query)
if err := c.Bind().Query(out); err != nil {
return err // you should have entered right name!
}
return c.SendString(out.Name)
})
app.Listen(":3000")
app.Listen(":3000")
}
// Run tests with the following curl command:
// curl "http://localhost:3000/?name=efe"
```
```

View File

@ -40,6 +40,10 @@ func (b *formBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error {
}
})
if err != nil {
return err
}
return parse(b.Name(), out, data)
}

View File

@ -12,9 +12,9 @@ import (
// ParserConfig form decoder config for SetParserDecoder
type ParserConfig struct {
IgnoreUnknownKeys bool
SetAliasTag string
ParserType []ParserType
IgnoreUnknownKeys bool
ZeroEmpty bool
}

View File

@ -7,9 +7,7 @@ import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
urlpkg "net/url"
"os"
"path/filepath"
"sync"
@ -20,12 +18,10 @@ import (
"github.com/gofiber/utils/v2"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpproxy"
)
var (
ErrInvalidProxyURL = errors.New("invalid proxy url scheme")
ErrFailedToAppendCert = errors.New("failed to append certificate")
)
var ErrFailedToAppendCert = errors.New("failed to append certificate")
// The Client is used to create a Fiber Client with
// client-level settings that apply to all requests
@ -34,21 +30,29 @@ var (
// Fiber Client also provides an option to override
// or merge most of the client settings at the request.
type Client struct {
mu sync.RWMutex
// logger
logger log.CommonLogger
fasthttp *fasthttp.Client
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
jsonMarshal utils.JSONMarshal
jsonUnmarshal utils.JSONUnmarshal
xmlMarshal utils.XMLMarshal
xmlUnmarshal utils.XMLUnmarshal
cookieJar *CookieJar
// retry
retryConfig *RetryConfig
baseURL string
userAgent string
referer string
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
debug bool
timeout time.Duration
// user defined request hooks
userRequestHooks []RequestHook
@ -62,21 +66,11 @@ type Client struct {
// client package defined response hooks
builtinResponseHooks []ResponseHook
jsonMarshal utils.JSONMarshal
jsonUnmarshal utils.JSONUnmarshal
xmlMarshal utils.XMLMarshal
xmlUnmarshal utils.XMLUnmarshal
timeout time.Duration
cookieJar *CookieJar
mu sync.RWMutex
// proxy
proxyURL string
// retry
retryConfig *RetryConfig
// logger
logger log.CommonLogger
debug bool
}
// R raise a request from the client.
@ -228,16 +222,7 @@ func (c *Client) SetRootCertificateFromString(pem string) *Client {
// SetProxyURL sets proxy url in client. It will apply via core to hostclient.
func (c *Client) SetProxyURL(proxyURL string) error {
pURL, err := urlpkg.Parse(proxyURL)
if err != nil {
return fmt.Errorf("client: %w", err)
}
if pURL.Scheme != "http" && pURL.Scheme != "https" {
return fmt.Errorf("client: %w", ErrInvalidProxyURL)
}
c.proxyURL = pURL.String()
c.fasthttp.Dial = fasthttpproxy.FasthttpHTTPDialer(proxyURL)
return nil
}
@ -581,7 +566,6 @@ func (c *Client) Reset() {
c.timeout = 0
c.userAgent = ""
c.referer = ""
c.proxyURL = ""
c.retryConfig = nil
c.debug = false
@ -604,19 +588,20 @@ func (c *Client) Reset() {
type Config struct {
Ctx context.Context //nolint:containedctx // It's needed to be stored in the config.
UserAgent string
Referer string
Body any
Header map[string]string
Param map[string]string
Cookie map[string]string
PathParam map[string]string
FormData map[string]string
UserAgent string
Referer string
File []*File
Timeout time.Duration
MaxRedirects int
Body any
FormData map[string]string
File []*File
}
// setConfigToRequest Set the parameters passed via Config to Request.
@ -672,7 +657,7 @@ func setConfigToRequest(req *Request, config ...Config) {
return
}
if cfg.File != nil && len(cfg.File) != 0 {
if len(cfg.File) != 0 {
req.AddFiles(cfg.File...)
return
}

View File

@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
)
func startTestServerWithPort(t *testing.T, beforeStarting func(app *fiber.App)) (*fiber.App, string) {
@ -835,8 +836,8 @@ func Test_Client_Cookie(t *testing.T) {
t.Run("set cookies with struct", func(t *testing.T) {
t.Parallel()
type args struct {
CookieInt int `cookie:"int"`
CookieString string `cookie:"string"`
CookieInt int `cookie:"int"`
}
req := New().SetCookiesWithStruct(&args{
@ -1087,12 +1088,12 @@ func Test_Client_QueryParam(t *testing.T) {
t.Parallel()
type args struct {
TInt int
TString string
TFloat float64
TBool bool
TSlice []string
TIntSlice []int `param:"int_slice"`
TInt int
TFloat float64
TBool bool
}
p := New()
@ -1195,8 +1196,8 @@ func Test_Client_PathParam(t *testing.T) {
t.Run("set path params with struct", func(t *testing.T) {
t.Parallel()
type args struct {
CookieInt int `path:"int"`
CookieString string `path:"string"`
CookieInt int `path:"int"`
}
req := New().SetPathParamsWithStruct(&args{
@ -1422,7 +1423,7 @@ func Test_Set_Config_To_Request(t *testing.T) {
key := struct{}{}
ctx := context.Background()
ctx = context.WithValue(ctx, key, "v1")
ctx = context.WithValue(ctx, key, "v1") //nolint: staticcheck // not needed for tests
req := AcquireRequest()
@ -1539,11 +1540,53 @@ func Test_Client_SetProxyURL(t *testing.T) {
app, dial, start := createHelperServer(t)
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("hello world")
return c.SendString(c.Get("isProxy"))
})
go start()
fasthttpClient := &fasthttp.Client{
Dial: dial,
NoDefaultUserAgentHeader: true,
DisablePathNormalizing: true,
}
// Create a simple proxy sever
proxyServer := fiber.New()
proxyServer.Use("*", func(c fiber.Ctx) error {
req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse()
req.SetRequestURI(c.BaseURL())
req.Header.SetMethod(fasthttp.MethodGet)
c.Request().Header.VisitAll(func(key, value []byte) {
req.Header.AddBytesKV(key, value)
})
req.Header.Set("isProxy", "true")
if err := fasthttpClient.Do(req, resp); err != nil {
return err
}
c.Status(resp.StatusCode())
c.Context().SetBody(resp.Body())
return nil
})
addrChan := make(chan string)
go func() {
assert.NoError(t, proxyServer.Listen(":0", fiber.ListenConfig{
DisableStartupMessage: true,
ListenerAddrFunc: func(addr net.Addr) {
addrChan <- addr.String()
},
}))
}()
t.Cleanup(func() {
require.NoError(t, app.Shutdown())
})
@ -1552,31 +1595,27 @@ func Test_Client_SetProxyURL(t *testing.T) {
t.Run("success", func(t *testing.T) {
t.Parallel()
client := New().SetDial(dial)
err := client.SetProxyURL("http://test.com")
require.NoError(t, err)
_, err = client.Get("http://localhost:3000")
require.NoError(t, err)
})
t.Run("wrong url", func(t *testing.T) {
t.Parallel()
client := New()
err := client.SetProxyURL(<-addrChan)
err := client.SetProxyURL(":this is not a url")
require.NoError(t, err)
require.Error(t, err)
resp, err := client.Get("http://localhost:3000")
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode())
require.Equal(t, "true", string(resp.Body()))
})
t.Run("error", func(t *testing.T) {
t.Parallel()
client := New()
err := client.SetProxyURL("htgdftp://test.com")
err := client.SetProxyURL(":this is not a proxy")
require.NoError(t, err)
_, err = client.Get("http://localhost:3000")
require.Error(t, err)
})
}

View File

@ -36,8 +36,8 @@ func ReleaseCookieJar(c *CookieJar) {
// CookieJar manages cookie storage. It is used by the client to store cookies.
type CookieJar struct {
mu sync.Mutex
hostCookies map[string][]*fasthttp.Cookie
mu sync.Mutex
}
// Get returns the cookies stored from a specific domain.

View File

@ -22,8 +22,8 @@ func Test_AddMissing_Port(t *testing.T) {
}
tests := []struct {
name string
args args
want string
args args
}{
{
name: "do anything",
@ -49,7 +49,6 @@ func Test_AddMissing_Port(t *testing.T) {
},
}
for _, tt := range tests {
tt := tt // create a new 'tt' variable for the goroutine
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.want, addMissingPort(tt.args.addr, tt.args.isTLS))

View File

@ -35,7 +35,6 @@ func Test_Rand_String(t *testing.T) {
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := randString(tt.args)
@ -188,7 +187,7 @@ func Test_Parser_Request_URL(t *testing.T) {
flag1 = true
case "foo2":
flag2 = true
case "foo":
case "foo": //nolint:goconst // test
flag3 = true
}
}

View File

@ -40,28 +40,30 @@ var ErrClientNil = errors.New("client can not be nil")
// Request is a struct which contains the request data.
type Request struct {
url string
method string
userAgent string
boundary string
referer string
ctx context.Context //nolint:containedctx // It's needed to be stored in the request.
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
ctx context.Context //nolint:containedctx // It's needed to be stored in the request.
body any
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
client *Client
formData *FormData
RawRequest *fasthttp.Request
url string
method string
userAgent string
boundary string
referer string
files []*File
timeout time.Duration
maxRedirects int
client *Client
body any
formData *FormData
files []*File
bodyType bodyType
RawRequest *fasthttp.Request
}
// Method returns http method in request.
@ -782,10 +784,10 @@ func (f *FormData) Reset() {
// File is a struct which support send files via request.
type File struct {
reader io.ReadCloser
name string
fieldName string
path string
reader io.ReadCloser
}
// SetName method sets file name.

View File

@ -84,7 +84,7 @@ func Test_Request_Context(t *testing.T) {
require.Nil(t, ctx.Value(key))
ctx = context.WithValue(ctx, key, "string")
ctx = context.WithValue(ctx, key, "string") //nolint: staticcheck // not needed for tests
req.SetContext(ctx)
ctx = req.Context()
@ -222,12 +222,12 @@ func Test_Request_QueryParam(t *testing.T) {
t.Parallel()
type args struct {
TInt int
TString string
TFloat float64
TBool bool
TSlice []string
TIntSlice []int `param:"int_slice"`
TInt int
TFloat float64
TBool bool
}
p := AcquireRequest()
@ -334,8 +334,8 @@ func Test_Request_Cookie(t *testing.T) {
t.Run("set cookies with struct", func(t *testing.T) {
t.Parallel()
type args struct {
CookieInt int `cookie:"int"`
CookieString string `cookie:"string"`
CookieInt int `cookie:"int"`
}
req := AcquireRequest().SetCookiesWithStruct(&args{
@ -396,8 +396,8 @@ func Test_Request_PathParam(t *testing.T) {
t.Run("set path params with struct", func(t *testing.T) {
t.Parallel()
type args struct {
CookieInt int `path:"int"`
CookieString string `path:"string"`
CookieInt int `path:"int"`
}
req := AcquireRequest().SetPathParamsWithStruct(&args{
@ -510,12 +510,12 @@ func Test_Request_FormData(t *testing.T) {
t.Parallel()
type args struct {
TInt int
TString string
TFloat float64
TBool bool
TSlice []string
TIntSlice []int `form:"int_slice"`
TInt int
TFloat float64
TBool bool
}
p := AcquireRequest()
@ -1299,13 +1299,13 @@ func Test_SetValWithStruct(t *testing.T) {
// test SetValWithStruct vai QueryParam struct.
type args struct {
unexport int
TInt int
TString string
TFloat float64
TBool bool
TSlice []string
TIntSlice []int `param:"int_slice"`
unexport int
TInt int
TFloat float64
TBool bool
}
t.Run("the struct should be applied", func(t *testing.T) {
@ -1340,7 +1340,7 @@ func Test_SetValWithStruct(t *testing.T) {
require.True(t, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "bar" {
if string(v) == "bar" { //nolint:goconst // test
return true
}
}
@ -1453,13 +1453,13 @@ func Test_SetValWithStruct(t *testing.T) {
func Benchmark_SetValWithStruct(b *testing.B) {
// test SetValWithStruct vai QueryParam struct.
type args struct {
unexport int
TInt int
TString string
TFloat float64
TBool bool
TSlice []string
TIntSlice []int `param:"int_slice"`
unexport int
TInt int
TFloat float64
TBool bool
}
b.Run("the struct should be applied", func(b *testing.B) {
@ -1603,8 +1603,8 @@ func Benchmark_SetValWithStruct(b *testing.B) {
require.Empty(b, string(p.Peek("TInt")))
require.Empty(b, string(p.Peek("TString")))
require.Empty(b, string(p.Peek("TFloat")))
require.Empty(b, len(p.PeekMulti("TSlice")))
require.Empty(b, len(p.PeekMulti("int_slice")))
require.Empty(b, p.PeekMulti("TSlice"))
require.Empty(b, p.PeekMulti("int_slice"))
})
b.Run("error type should ignore", func(b *testing.B) {

View File

@ -8,7 +8,6 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
"sync"
"github.com/gofiber/utils/v2"
@ -19,9 +18,9 @@ import (
type Response struct {
client *Client
request *Request
cookie []*fasthttp.Cookie
RawResponse *fasthttp.Response
cookie []*fasthttp.Cookie
}
// setClient method sets client object in response instance.
@ -68,7 +67,7 @@ func (r *Response) Body() []byte {
// String method returns the body of the server response as String.
func (r *Response) String() string {
return strings.TrimSpace(string(r.Body()))
return utils.Trim(string(r.Body()), ' ')
}
// JSON method will unmarshal body to json.

104
ctx.go
View File

@ -50,24 +50,24 @@ const userContextKey contextKey = 0 // __local_user_context__
type DefaultCtx struct {
app *App // Reference to *App
route *Route // Reference to *Route
indexRoute int // Index of the current route
indexHandler int // Index of the current handler
method string // HTTP method
methodINT int // HTTP method INT equivalent
baseURI string // HTTP base uri
path string // HTTP path with the modifications by the configuration -> string copy from pathBuffer
pathBuffer []byte // HTTP path buffer
detectionPath string // Route detection path -> string copy from detectionPathBuffer
detectionPathBuffer []byte // HTTP detectionPath buffer
treePath string // Path for the search in the tree
pathOriginal string // Original HTTP path
values [maxParams]string // Route parameter values
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
matched bool // Non use route matched
viewBindMap sync.Map // Default view map to bind template engine
bind *Bind // Default bind reference
redirect *Redirect // Default redirect reference
redirectionMessages []string // Messages of the previous redirect
values [maxParams]string // Route parameter values
viewBindMap sync.Map // Default view map to bind template engine
method string // HTTP method
baseURI string // HTTP base uri
path string // HTTP path with the modifications by the configuration -> string copy from pathBuffer
detectionPath string // Route detection path -> string copy from detectionPathBuffer
treePath string // Path for the search in the tree
pathOriginal string // Original HTTP path
pathBuffer []byte // HTTP path buffer
detectionPathBuffer []byte // HTTP detectionPath buffer
flashMessages redirectionMsgs // Flash messages
indexRoute int // Index of the current route
indexHandler int // Index of the current handler
methodINT int // HTTP method INT equivalent
matched bool // Non use route matched
}
// SendFile defines configuration options when to transfer file with SendFile.
@ -112,8 +112,8 @@ type SendFile struct {
// sendFileStore is used to keep the SendFile configuration and the handler.
type sendFileStore struct {
handler fasthttp.RequestHandler
config SendFile
cacheControlValue string
config SendFile
}
// compareConfig compares the current SendFile config with the new one
@ -175,15 +175,15 @@ type RangeSet struct {
// Cookie data for c.Cookie
type Cookie struct {
Expires time.Time `json:"expires"` // The expiration date of the cookie
Name string `json:"name"` // The name of the cookie
Value string `json:"value"` // The value of the cookie
Path string `json:"path"` // Specifies a URL path which is allowed to receive the cookie
Domain string `json:"domain"` // Specifies the domain which is allowed to receive the cookie
SameSite string `json:"same_site"` // Controls whether or not a cookie is sent with cross-site requests
MaxAge int `json:"max_age"` // The maximum age (in seconds) of the cookie
Expires time.Time `json:"expires"` // The expiration date of the cookie
Secure bool `json:"secure"` // Indicates that the cookie should only be transmitted over a secure HTTPS connection
HTTPOnly bool `json:"http_only"` // Indicates that the cookie is accessible only through the HTTP protocol
SameSite string `json:"same_site"` // Controls whether or not a cookie is sent with cross-site requests
Partitioned bool `json:"partitioned"` // Indicates if the cookie is stored in a partitioned cookie jar
SessionOnly bool `json:"session_only"` // Indicates if the cookie is a session-only cookie
}
@ -196,8 +196,8 @@ type Views interface {
// ResFmt associates a Content Type to a fiber.Handler for c.Format
type ResFmt struct {
MediaType string
Handler func(Ctx) error
MediaType string
}
// Accepts checks if the specified extensions or content types are acceptable.
@ -273,10 +273,7 @@ func (c *DefaultCtx) BaseURL() string {
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (c *DefaultCtx) BodyRaw() []byte {
if c.app.config.Immutable {
return utils.CopyBytes(c.fasthttp.Request.Body())
}
return c.fasthttp.Request.Body()
return c.getBody()
}
func (c *DefaultCtx) tryDecodeBodyInOrder(
@ -339,21 +336,19 @@ func (c *DefaultCtx) Body() []byte {
encodingOrder = []string{"", "", ""}
)
// faster than peek
c.Request().Header.VisitAll(func(key, value []byte) {
if c.app.getString(key) == HeaderContentEncoding {
headerEncoding = c.app.getString(value)
}
})
// Get Content-Encoding header
headerEncoding = utils.UnsafeString(c.Request().Header.ContentEncoding())
// If no encoding is provided, return the original body
if len(headerEncoding) == 0 {
return c.getBody()
}
// Split and get the encodings list, in order to attend the
// rule defined at: https://www.rfc-editor.org/rfc/rfc9110#section-8.4-5
encodingOrder = getSplicedStrList(headerEncoding, encodingOrder)
if len(encodingOrder) == 0 {
if c.app.config.Immutable {
return utils.CopyBytes(c.fasthttp.Request.Body())
}
return c.fasthttp.Request.Body()
return c.getBody()
}
var decodesRealized uint8
@ -778,7 +773,7 @@ iploop:
i++
}
s := strings.TrimRight(headerValue[i:j], " ")
s := utils.TrimRight(headerValue[i:j], ' ')
if c.app.config.EnableIPValidation {
// Skip validation if IP is clearly not IPv4/IPv6, otherwise validate without allocations
@ -828,7 +823,7 @@ func (c *DefaultCtx) extractIPFromHeader(header string) string {
i++
}
s := strings.TrimRight(headerValue[i:j], " ")
s := utils.TrimRight(headerValue[i:j], ' ')
if c.app.config.EnableIPValidation {
if (!v6 && !v4) || (v6 && !utils.IsIPv6(s)) || (v4 && !utils.IsIPv4(s)) {
@ -862,7 +857,7 @@ func (c *DefaultCtx) Is(extension string) bool {
}
return strings.HasPrefix(
strings.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), " "),
utils.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), ' '),
extensionHeader,
)
}
@ -939,7 +934,7 @@ func (c *DefaultCtx) Links(link ...string) {
bb.WriteString(`; rel="` + link[i] + `",`)
}
}
c.setCanonical(HeaderLink, strings.TrimRight(c.app.getString(bb.Bytes()), ","))
c.setCanonical(HeaderLink, utils.TrimRight(c.app.getString(bb.Bytes()), ','))
bytebufferpool.Put(bb)
}
@ -1023,19 +1018,20 @@ func (c *DefaultCtx) ClientHelloInfo() *tls.ClientHelloInfo {
func (c *DefaultCtx) Next() error {
// Increment handler index
c.indexHandler++
var err error
// Did we execute all route handlers?
if c.indexHandler < len(c.route.Handlers) {
// Continue route stack
err = c.route.Handlers[c.indexHandler](c)
} else {
// Continue handler stack
if c.app.newCtxFunc != nil {
_, err = c.app.nextCustom(c)
} else {
_, err = c.app.next(c)
}
return c.route.Handlers[c.indexHandler](c)
}
// Continue handler stack
if c.app.newCtxFunc != nil {
_, err := c.app.nextCustom(c)
return err
}
_, err := c.app.next(c)
return err
}
@ -1284,8 +1280,8 @@ func (c *DefaultCtx) Range(size int) (Range, error) {
Start int
End int
}{
start,
end,
Start: start,
End: end,
})
}
if len(rangeData.Ranges) < 1 {
@ -1809,7 +1805,7 @@ func (c *DefaultCtx) configDependentPaths() {
}
// If StrictRouting is disabled, we strip all trailing slashes
if !c.app.config.StrictRouting && len(c.detectionPathBuffer) > 1 && c.detectionPathBuffer[len(c.detectionPathBuffer)-1] == '/' {
c.detectionPathBuffer = bytes.TrimRight(c.detectionPathBuffer, "/")
c.detectionPathBuffer = utils.TrimRight(c.detectionPathBuffer, '/')
}
c.detectionPath = c.app.getString(c.detectionPathBuffer)
@ -1900,7 +1896,7 @@ func (c *DefaultCtx) release() {
c.route = nil
c.fasthttp = nil
c.bind = nil
c.redirectionMessages = c.redirectionMessages[:0]
c.flashMessages = c.flashMessages[:0]
c.viewBindMap = sync.Map{}
if c.redirect != nil {
ReleaseRedirect(c.redirect)
@ -1908,6 +1904,14 @@ func (c *DefaultCtx) release() {
}
}
func (c *DefaultCtx) getBody() []byte {
if c.app.config.Immutable {
return utils.CopyBytes(c.fasthttp.Request.Body())
}
return c.fasthttp.Request.Body()
}
// Methods to use with next stack.
func (c *DefaultCtx) getMethodINT() int {
return c.methodINT

View File

@ -330,6 +330,7 @@ type Ctx interface {
Reset(fctx *fasthttp.RequestCtx)
// Release is a method to reset context fields when to use ReleaseCtx()
release()
getBody() []byte
// Methods to use with next stack.
getMethodINT() int
getIndexRoute() int

View File

@ -356,6 +356,26 @@ func Test_Ctx_Body(t *testing.T) {
require.Equal(t, []byte("john=doe"), c.Body())
}
// go test -run Test_Ctx_BodyRaw
func Test_Ctx_BodyRaw(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
c.Request().SetBodyRaw([]byte("john=doe"))
require.Equal(t, []byte("john=doe"), c.BodyRaw())
}
// go test -run Test_Ctx_BodyRaw_Immutable
func Test_Ctx_BodyRaw_Immutable(t *testing.T) {
t.Parallel()
app := New(Config{Immutable: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
c.Request().SetBodyRaw([]byte("john=doe"))
require.Equal(t, []byte("john=doe"), c.BodyRaw())
}
// go test -v -run=^$ -bench=Benchmark_Ctx_Body -benchmem -count=4
func Benchmark_Ctx_Body(b *testing.B) {
const input = "john=doe"
@ -373,6 +393,40 @@ func Benchmark_Ctx_Body(b *testing.B) {
require.Equal(b, []byte(input), c.Body())
}
// go test -v -run=^$ -bench=Benchmark_Ctx_BodyRaw -benchmem -count=4
func Benchmark_Ctx_BodyRaw(b *testing.B) {
const input = "john=doe"
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
c.Request().SetBodyRaw([]byte(input))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = c.BodyRaw()
}
require.Equal(b, []byte(input), c.BodyRaw())
}
// go test -v -run=^$ -bench=Benchmark_Ctx_BodyRaw_Immutable -benchmem -count=4
func Benchmark_Ctx_BodyRaw_Immutable(b *testing.B) {
const input = "john=doe"
app := New(Config{Immutable: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
c.Request().SetBodyRaw([]byte(input))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = c.BodyRaw()
}
require.Equal(b, []byte(input), c.BodyRaw())
}
// go test -run Test_Ctx_Body_Immutable
func Test_Ctx_Body_Immutable(t *testing.T) {
t.Parallel()
@ -509,8 +563,8 @@ func Benchmark_Ctx_Body_With_Compression(b *testing.B) {
}
)
compressionTests := []struct {
contentEncoding string
compressWriter func([]byte) ([]byte, error)
contentEncoding string
}{
{
contentEncoding: "gzip",
@ -702,8 +756,8 @@ func Benchmark_Ctx_Body_With_Compression_Immutable(b *testing.B) {
}
)
compressionTests := []struct {
contentEncoding string
compressWriter func([]byte) ([]byte, error)
contentEncoding string
}{
{
contentEncoding: "gzip",
@ -813,7 +867,7 @@ func Test_Ctx_UserContext(t *testing.T) {
t.Parallel()
testKey := struct{}{}
testValue := "Test Value"
ctx := context.WithValue(context.Background(), testKey, testValue)
ctx := context.WithValue(context.Background(), testKey, testValue) //nolint: staticcheck // not needed for tests
require.Equal(t, testValue, ctx.Value(testKey))
})
}
@ -826,7 +880,7 @@ func Test_Ctx_SetUserContext(t *testing.T) {
testKey := struct{}{}
testValue := "Test Value"
ctx := context.WithValue(context.Background(), testKey, testValue)
ctx := context.WithValue(context.Background(), testKey, testValue) //nolint: staticcheck // not needed for tests
c.SetUserContext(ctx)
require.Equal(t, testValue, c.UserContext().Value(testKey))
}
@ -846,7 +900,7 @@ func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) {
}
input := utils.CopyString(Query(c, "input", "NO_VALUE"))
ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input))
ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input)) //nolint: staticcheck // not needed for tests
c.SetUserContext(ctx)
return c.Status(StatusOK).SendString(fmt.Sprintf("resp_%s_returned", input))
@ -854,7 +908,6 @@ func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) {
// Consecutive Requests
for i := 1; i <= 10; i++ {
i := i
t.Run(fmt.Sprintf("request_%d", i), func(t *testing.T) {
t.Parallel()
resp, err := app.Test(httptest.NewRequest(MethodGet, fmt.Sprintf("/?input=%d", i), nil))
@ -966,7 +1019,7 @@ func Test_Ctx_Format(t *testing.T) {
fmts := []ResFmt{}
for _, t := range types {
t := utils.CopyString(t)
fmts = append(fmts, ResFmt{t, func(_ Ctx) error {
fmts = append(fmts, ResFmt{MediaType: t, Handler: func(_ Ctx) error {
accepted = t
return nil
}})
@ -988,11 +1041,11 @@ func Test_Ctx_Format(t *testing.T) {
require.NotEqual(t, StatusNotAcceptable, c.Response().StatusCode())
myError := errors.New("this is an error")
err = c.Format(ResFmt{"text/html", func(_ Ctx) error { return myError }})
err = c.Format(ResFmt{MediaType: "text/html", Handler: func(_ Ctx) error { return myError }})
require.ErrorIs(t, err, myError)
c.Request().Header.Set(HeaderAccept, "application/json")
err = c.Format(ResFmt{"text/html", func(c Ctx) error { return c.SendStatus(StatusOK) }})
err = c.Format(ResFmt{MediaType: "text/html", Handler: func(c Ctx) error { return c.SendStatus(StatusOK) }})
require.Equal(t, StatusNotAcceptable, c.Response().StatusCode())
require.NoError(t, err)
@ -1022,10 +1075,10 @@ func Benchmark_Ctx_Format(b *testing.B) {
b.Run("with arg allocation", func(b *testing.B) {
for n := 0; n < b.N; n++ {
err = c.Format(
ResFmt{"application/xml", fail},
ResFmt{"text/html", fail},
ResFmt{"text/plain;format=fixed", fail},
ResFmt{"text/plain;format=flowed", ok},
ResFmt{MediaType: "application/xml", Handler: fail},
ResFmt{MediaType: "text/html", Handler: fail},
ResFmt{MediaType: "text/plain;format=fixed", Handler: fail},
ResFmt{MediaType: "text/plain;format=flowed", Handler: ok},
)
}
require.NoError(b, err)
@ -1033,10 +1086,10 @@ func Benchmark_Ctx_Format(b *testing.B) {
b.Run("pre-allocated args", func(b *testing.B) {
offers := []ResFmt{
{"application/xml", fail},
{"text/html", fail},
{"text/plain;format=fixed", fail},
{"text/plain;format=flowed", ok},
{MediaType: "application/xml", Handler: fail},
{MediaType: "text/html", Handler: fail},
{MediaType: "text/plain;format=fixed", Handler: fail},
{MediaType: "text/plain;format=flowed", Handler: ok},
}
for n := 0; n < b.N; n++ {
err = c.Format(offers...)
@ -1047,8 +1100,8 @@ func Benchmark_Ctx_Format(b *testing.B) {
c.Request().Header.Set("Accept", "text/plain")
b.Run("text/plain", func(b *testing.B) {
offers := []ResFmt{
{"application/xml", fail},
{"text/plain", ok},
{MediaType: "application/xml", Handler: fail},
{MediaType: "text/plain", Handler: ok},
}
for n := 0; n < b.N; n++ {
err = c.Format(offers...)
@ -1059,9 +1112,9 @@ func Benchmark_Ctx_Format(b *testing.B) {
c.Request().Header.Set("Accept", "json")
b.Run("json", func(b *testing.B) {
offers := []ResFmt{
{"xml", fail},
{"html", fail},
{"json", ok},
{MediaType: "xml", Handler: fail},
{MediaType: "html", Handler: fail},
{MediaType: "json", Handler: ok},
}
for n := 0; n < b.N; n++ {
err = c.Format(offers...)
@ -1123,9 +1176,9 @@ func Test_Ctx_AutoFormat_Struct(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Message struct {
Recipients []string
Sender string `xml:"sender,attr"`
Urgency int `xml:"urgency,attr"`
Recipients []string
Urgency int `xml:"urgency,attr"`
}
data := Message{
Recipients: []string{"Alice", "Bob"},
@ -1137,7 +1190,7 @@ func Test_Ctx_AutoFormat_Struct(t *testing.T) {
err := c.AutoFormat(data)
require.NoError(t, err)
require.Equal(t,
`{"Recipients":["Alice","Bob"],"Sender":"Carol","Urgency":3}`,
`{"Sender":"Carol","Recipients":["Alice","Bob"],"Urgency":3}`,
string(c.Response().Body()),
)
@ -1370,11 +1423,11 @@ func Test_Ctx_Binders(t *testing.T) {
}
type TestStruct struct {
Name string
NameWithDefault string `json:"name2" xml:"Name2" form:"name2" cookie:"name2" query:"name2" params:"name2" header:"Name2"`
TestEmbeddedStruct
Name string
Class int
NameWithDefault string `json:"name2" xml:"Name2" form:"name2" cookie:"name2" query:"name2" params:"name2" header:"Name2"`
ClassWithDefault int `json:"class2" xml:"Class2" form:"class2" cookie:"class2" query:"class2" params:"class2" header:"Class2"`
ClassWithDefault int `json:"class2" xml:"Class2" form:"class2" cookie:"class2" query:"class2" params:"class2" header:"Class2"`
}
withValues := func(t *testing.T, actionFn func(c Ctx, testStruct *TestStruct) error) {
@ -2141,11 +2194,11 @@ func Test_Ctx_Locals_GenericCustomStruct(t *testing.T) {
app := New()
app.Use(func(c Ctx) error {
Locals[User](c, "user", User{"john", 18})
Locals[User](c, "user", User{name: "john", age: 18})
return c.Next()
})
app.Use("/test", func(c Ctx) error {
require.Equal(t, User{"john", 18}, Locals[User](c, "user"))
require.Equal(t, User{name: "john", age: 18}, Locals[User](c, "user"))
return nil
})
resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil))
@ -2697,13 +2750,13 @@ func Test_Ctx_Range(t *testing.T) {
testRange("bytes=")
testRange("bytes=500=")
testRange("bytes=500-300")
testRange("bytes=a-700", RangeSet{300, 999})
testRange("bytes=500-b", RangeSet{500, 999})
testRange("bytes=500-1000", RangeSet{500, 999})
testRange("bytes=500-700", RangeSet{500, 700})
testRange("bytes=0-0,2-1000", RangeSet{0, 0}, RangeSet{2, 999})
testRange("bytes=0-99,450-549,-100", RangeSet{0, 99}, RangeSet{450, 549}, RangeSet{900, 999})
testRange("bytes=500-700,601-999", RangeSet{500, 700}, RangeSet{601, 999})
testRange("bytes=a-700", RangeSet{Start: 300, End: 999})
testRange("bytes=500-b", RangeSet{Start: 500, End: 999})
testRange("bytes=500-1000", RangeSet{Start: 500, End: 999})
testRange("bytes=500-700", RangeSet{Start: 500, End: 700})
testRange("bytes=0-0,2-1000", RangeSet{Start: 0, End: 0}, RangeSet{Start: 2, End: 999})
testRange("bytes=0-99,450-549,-100", RangeSet{Start: 0, End: 99}, RangeSet{Start: 450, End: 549}, RangeSet{Start: 900, End: 999})
testRange("bytes=500-700,601-999", RangeSet{Start: 500, End: 700}, RangeSet{Start: 601, End: 999})
}
// go test -v -run=^$ -bench=Benchmark_Ctx_Range -benchmem -count=4
@ -2717,10 +2770,10 @@ func Benchmark_Ctx_Range(b *testing.B) {
start int
end int
}{
{"bytes=-700", 300, 999},
{"bytes=500-", 500, 999},
{"bytes=500-1000", 500, 999},
{"bytes=0-700,800-1000", 0, 700},
{str: "bytes=-700", start: 300, end: 999},
{str: "bytes=500-", start: 500, end: 999},
{str: "bytes=500-1000", start: 500, end: 999},
{str: "bytes=0-700,800-1000", start: 0, end: 700},
}
for _, tc := range testCases {
@ -3068,8 +3121,6 @@ func Test_Static_Compress(t *testing.T) {
// Note: deflate is not supported by fasthttp.FS
algorithms := []string{"zstd", "gzip", "br"}
for _, algo := range algorithms {
algo := algo
t.Run(algo+"_compression", func(t *testing.T) {
t.Parallel()
@ -3099,23 +3150,25 @@ func Test_Ctx_SendFile_Compress_CheckCompressed(t *testing.T) {
expectedFileContent, err := io.ReadAll(f)
require.NoError(t, err)
sendFileBodyReader := func(compression string) []byte {
reqCtx := &fasthttp.RequestCtx{}
reqCtx.Request.Header.Add(HeaderAcceptEncoding, compression)
sendFileBodyReader := func(compression string) ([]byte, error) {
t.Helper()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
c.Request().Header.Add(HeaderAcceptEncoding, compression)
c := app.AcquireCtx(reqCtx)
err = c.SendFile("./ctx.go", SendFile{
err := c.SendFile("./ctx.go", SendFile{
Compress: true,
})
require.NoError(t, err)
return c.Response().Body()
return c.Response().Body(), err
}
t.Run("gzip", func(t *testing.T) {
t.Parallel()
body, err := fasthttp.AppendGunzipBytes(nil, sendFileBodyReader("gzip"))
b, err := sendFileBodyReader("gzip")
require.NoError(t, err)
body, err := fasthttp.AppendGunzipBytes(nil, b)
require.NoError(t, err)
require.Equal(t, expectedFileContent, body)
@ -3124,7 +3177,9 @@ func Test_Ctx_SendFile_Compress_CheckCompressed(t *testing.T) {
t.Run("zstd", func(t *testing.T) {
t.Parallel()
body, err := fasthttp.AppendUnzstdBytes(nil, sendFileBodyReader("zstd"))
b, err := sendFileBodyReader("zstd")
require.NoError(t, err)
body, err := fasthttp.AppendUnzstdBytes(nil, b)
require.NoError(t, err)
require.Equal(t, expectedFileContent, body)
@ -3133,7 +3188,9 @@ func Test_Ctx_SendFile_Compress_CheckCompressed(t *testing.T) {
t.Run("br", func(t *testing.T) {
t.Parallel()
body, err := fasthttp.AppendUnbrotliBytes(nil, sendFileBodyReader("br"))
b, err := sendFileBodyReader("br")
require.NoError(t, err)
body, err := fasthttp.AppendUnbrotliBytes(nil, b)
require.NoError(t, err)
require.Equal(t, expectedFileContent, body)
@ -3223,12 +3280,12 @@ func Test_Ctx_SendFile_Multiple(t *testing.T) {
body string
contentDisposition string
}{
{"/test?file=1", "type DefaultCtx struct", ""},
{"/test?file=2", "type App struct", ""},
{"/test?file=3", "type DefaultCtx struct", "attachment"},
{"/test?file=4", "Test_App_MethodNotAllowed", ""},
{"/test2", "type DefaultCtx struct", "attachment"},
{"/test2", "type DefaultCtx struct", "attachment"},
{url: "/test?file=1", body: "type DefaultCtx struct", contentDisposition: ""},
{url: "/test?file=2", body: "type App struct", contentDisposition: ""},
{url: "/test?file=3", body: "type DefaultCtx struct", contentDisposition: "attachment"},
{url: "/test?file=4", body: "Test_App_MethodNotAllowed", contentDisposition: ""},
{url: "/test2", body: "type DefaultCtx struct", contentDisposition: "attachment"},
{url: "/test2", body: "type DefaultCtx struct", contentDisposition: "attachment"},
}
for _, tc := range testCases {
@ -3242,6 +3299,8 @@ func Test_Ctx_SendFile_Multiple(t *testing.T) {
require.Contains(t, string(body), tc.body)
}
app.sendfilesMutex.RLock()
defer app.sendfilesMutex.RUnlock()
require.Len(t, app.sendfiles, 3)
}
@ -3276,7 +3335,6 @@ func Test_Ctx_SendFile_Immutable(t *testing.T) {
}
for _, endpoint := range endpointsForTest {
endpoint := endpoint
t.Run(endpoint, func(t *testing.T) {
t.Parallel()
// 1st try

View File

@ -43,19 +43,19 @@ func (app *App) MountPath() string
```go title="Examples"
func main() {
app := fiber.New()
one := fiber.New()
two := fiber.New()
three := fiber.New()
app := fiber.New()
one := fiber.New()
two := fiber.New()
three := fiber.New()
two.Use("/three", three)
one.Use("/two", two)
app.Use("/one", one)
two.Use("/three", three)
one.Use("/two", two)
app.Use("/one", one)
one.MountPath() // "/one"
two.MountPath() // "/one/two"
three.MountPath() // "/one/two/three"
app.MountPath() // ""
one.MountPath() // "/one"
two.MountPath() // "/one/two"
three.MountPath() // "/one/two/three"
app.MountPath() // ""
}
```
@ -91,7 +91,7 @@ func main() {
### Route
Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware.
Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware.
Similar to [`Express`](https://expressjs.com/de/api.html#app.route).
@ -120,6 +120,7 @@ type Register interface {
Route(path string) Register
}
```
</details>
```go title="Examples"
@ -222,6 +223,7 @@ func main() {
]
]
```
</details>
### Name
@ -328,6 +330,7 @@ func main() {
null
]
```
</details>
### GetRoute
@ -347,10 +350,10 @@ func main() {
app.Get("/", handler).Name("index")
data, _ := json.MarshalIndent(app.GetRoute("index"), "", " ")
fmt.Print(string(data))
fmt.Print(string(data))
app.Listen(":3000")
app.Listen(":3000")
}
```
@ -365,6 +368,7 @@ func main() {
"params": null
}
```
</details>
### GetRoutes
@ -376,14 +380,15 @@ func (app *App) GetRoutes(filterUseOption ...bool) []Route
```
When filterUseOption equal to true, it will filter the routes registered by the middleware.
```go title="Examples"
func main() {
app := fiber.New()
app.Post("/", func (c fiber.Ctx) error {
return c.SendString("Hello, World!")
}).Name("index")
data, _ := json.MarshalIndent(app.GetRoutes(true), "", " ")
fmt.Print(string(data))
app := fiber.New()
app.Post("/", func (c fiber.Ctx) error {
return c.SendString("Hello, World!")
}).Name("index")
data, _ := json.MarshalIndent(app.GetRoutes(true), "", " ")
fmt.Print(string(data))
}
```
@ -400,6 +405,7 @@ func main() {
}
]
```
</details>
## Config
@ -436,12 +442,12 @@ func (app *App) NewCtxFunc(function func(app *App) CustomCtx)
```go title="Examples"
type CustomCtx struct {
DefaultCtx
DefaultCtx
}
// Custom method
func (c *CustomCtx) Params(key string, defaultValue ...string) string {
return "prefix_" + c.DefaultCtx.Params(key)
return "prefix_" + c.DefaultCtx.Params(key)
}
app := New()
@ -521,7 +527,6 @@ func (app *App) RegisterCustomConstraint(constraint CustomConstraint)
See [Custom Constraint](../guide/routing.md#custom-constraint) section for more information.
## SetTLSHandler
Use SetTLSHandler to set [ClientHelloInfo](https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2) when using TLS with Listener.
@ -568,3 +573,31 @@ Hooks is a method to return [hooks](./hooks.md) property.
```go title="Signature"
func (app *App) Hooks() *Hooks
```
## RebuildTree
The RebuildTree method is designed to rebuild the route tree and enable dynamic route registration. It returns a pointer to the App instance.
```go title="Signature"
func (app *App) RebuildTree() *App
```
**Note:** Use this method with caution. It is **not** thread-safe and calling it can be very performance-intensive, so it should be used sparingly and only in development mode. Avoid using it concurrently.
### Example Usage
Heres an example of how to define and register routes dynamically:
```go
app.Get("/define", func(c Ctx) error { // Define a new route dynamically
app.Get("/dynamically-defined", func(c Ctx) error { // Adding a dynamically defined route
return c.SendStatus(http.StatusOK)
})
app.RebuildTree() // Rebuild the route tree to register the new route
return c.SendStatus(http.StatusOK)
})
```
In this example, a new route is defined and then `RebuildTree()` is called to make sure the new route is registered and available.

View File

@ -15,14 +15,13 @@ Make copies or use the [**`Immutable`**](./ctx.md) setting instead. [Read more..
:::
## Binders
- [Body](#body)
- [Form](#form)
- [JSON](#json)
- [MultipartForm](#multipartform)
- [XML](#xml)
- [Form](#form)
- [JSON](#json)
- [MultipartForm](#multipartform)
- [XML](#xml)
- [Cookie](#cookie)
- [Header](#header)
- [Query](#query)
@ -80,7 +79,6 @@ app.Post("/", func(c fiber.Ctx) error {
// curl -X POST "http://localhost:3000/?name=john&pass=doe"
```
**The methods for the various bodies can also be used directly:**
#### Form
@ -89,7 +87,6 @@ Binds the request form body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a Form body with a field called Pass, you would use a struct field of `form:"pass"`.
```go title="Signature"
func (b *Bind) Form(out any) error
```
@ -125,7 +122,6 @@ Binds the request json body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called Pass, you would use a struct field of `json:"pass"`.
```go title="Signature"
func (b *Bind) JSON(out any) error
```
@ -162,7 +158,6 @@ Binds the request multipart form body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a MultipartForm body with a field called Pass, you would use a struct field of `form:"pass"`.
```go title="Signature"
func (b *Bind) MultipartForm(out any) error
```
@ -197,8 +192,7 @@ app.Post("/", func(c fiber.Ctx) error {
Binds the request xml form body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a XML body with a field called Pass, you would use a struct field of `xml:"pass"`.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse an XML body with a field called Pass, you would use a struct field of `xml:"pass"`.
```go title="Signature"
func (b *Bind) XML(out any) error
@ -229,7 +223,6 @@ app.Post("/", func(c fiber.Ctx) error {
// curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000
```
### Cookie
This method is similar to [Body-Binding](#body), but for cookie parameters.
@ -262,7 +255,6 @@ app.Get("/", func(c fiber.Ctx) error {
// curl.exe --cookie "name=Joseph; age=23; job=true" http://localhost:8000/
```
### Header
This method is similar to [Body-Binding](#body), but for request headers.
@ -298,7 +290,6 @@ app.Get("/", func(c fiber.Ctx) error {
// curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat"
```
### Query
This method is similar to [Body-Binding](#body), but for query parameters.
@ -342,7 +333,6 @@ app.Get("/", func(c fiber.Ctx) error {
For more parser settings please look here [Config](fiber.md#enablesplittingonparsers)
:::
### RespHeader
This method is similar to [Body-Binding](#body), but for response headers.
@ -467,7 +457,6 @@ It's default behavior of binder.
func (b *Bind) Should() *Bind
```
## SetParserDecoder
Allow you to config BodyParser/QueryParser decoder, base on schema's options, providing possibility to add custom type for parsing.
@ -542,7 +531,6 @@ app.Get("/query", func(c fiber.Ctx) error {
```
## Validation
Validation is also possible with the binding methods. You can specify your validation rules using the `validate` struct tag.
@ -585,6 +573,3 @@ app.Post("/", func(c fiber.Ctx) error {
}
})
```

View File

@ -5,7 +5,7 @@ description: Some constants for Fiber.
sidebar_position: 8
---
### HTTP methods were copied from net/http.
### HTTP methods were copied from net/http
```go
const (
@ -26,94 +26,94 @@ const (
```go
const (
MIMETextXML = "text/xml"
MIMETextHTML = "text/html"
MIMETextPlain = "text/plain"
MIMETextJavaScript = "text/javascript"
MIMETextCSS = "text/css"
MIMEApplicationXML = "application/xml"
MIMETextXML = "text/xml"
MIMETextHTML = "text/html"
MIMETextPlain = "text/plain"
MIMETextJavaScript = "text/javascript"
MIMETextCSS = "text/css"
MIMEApplicationXML = "application/xml"
MIMEApplicationJSON = "application/json"
MIMEApplicationJavaScript = "application/javascript"
MIMEApplicationForm = "application/x-www-form-urlencoded"
MIMEOctetStream = "application/octet-stream"
MIMEMultipartForm = "multipart/form-data"
MIMEApplicationJavaScript = "application/javascript"
MIMEApplicationForm = "application/x-www-form-urlencoded"
MIMEOctetStream = "application/octet-stream"
MIMEMultipartForm = "multipart/form-data"
MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8"
MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8"
MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8"
MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8"
MIMETextCSSCharsetUTF8 = "text/css; charset=utf-8"
MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8"
MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8"
MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8"
MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8"
MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8"
MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8"
MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8"
MIMETextCSSCharsetUTF8 = "text/css; charset=utf-8"
MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8"
MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8"
MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8"
)```
### HTTP status codes were copied from net/http.
```go
const (
StatusContinue = 100 // RFC 7231, 6.2.1
StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
StatusProcessing = 102 // RFC 2518, 10.1
StatusEarlyHints = 103 // RFC 8297
StatusOK = 200 // RFC 7231, 6.3.1
StatusCreated = 201 // RFC 7231, 6.3.2
StatusAccepted = 202 // RFC 7231, 6.3.3
StatusNonAuthoritativeInformation = 203 // RFC 7231, 6.3.4
StatusNoContent = 204 // RFC 7231, 6.3.5
StatusResetContent = 205 // RFC 7231, 6.3.6
StatusPartialContent = 206 // RFC 7233, 4.1
StatusMultiStatus = 207 // RFC 4918, 11.1
StatusAlreadyReported = 208 // RFC 5842, 7.1
StatusIMUsed = 226 // RFC 3229, 10.4.1
StatusMultipleChoices = 300 // RFC 7231, 6.4.1
StatusMovedPermanently = 301 // RFC 7231, 6.4.2
StatusFound = 302 // RFC 7231, 6.4.3
StatusSeeOther = 303 // RFC 7231, 6.4.4
StatusNotModified = 304 // RFC 7232, 4.1
StatusUseProxy = 305 // RFC 7231, 6.4.5
StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
StatusPermanentRedirect = 308 // RFC 7538, 3
StatusBadRequest = 400 // RFC 7231, 6.5.1
StatusUnauthorized = 401 // RFC 7235, 3.1
StatusPaymentRequired = 402 // RFC 7231, 6.5.2
StatusForbidden = 403 // RFC 7231, 6.5.3
StatusNotFound = 404 // RFC 7231, 6.5.4
StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5
StatusNotAcceptable = 406 // RFC 7231, 6.5.6
StatusProxyAuthRequired = 407 // RFC 7235, 3.2
StatusRequestTimeout = 408 // RFC 7231, 6.5.7
StatusConflict = 409 // RFC 7231, 6.5.8
StatusGone = 410 // RFC 7231, 6.5.9
StatusLengthRequired = 411 // RFC 7231, 6.5.10
StatusPreconditionFailed = 412 // RFC 7232, 4.2
StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11
StatusRequestURITooLong = 414 // RFC 7231, 6.5.12
StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13
StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
StatusExpectationFailed = 417 // RFC 7231, 6.5.14
StatusTeapot = 418 // RFC 7168, 2.3.3
StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2
StatusUnprocessableEntity = 422 // RFC 4918, 11.2
StatusLocked = 423 // RFC 4918, 11.3
StatusFailedDependency = 424 // RFC 4918, 11.4
StatusTooEarly = 425 // RFC 8470, 5.2.
StatusUpgradeRequired = 426 // RFC 7231, 6.5.15
StatusPreconditionRequired = 428 // RFC 6585, 3
StatusTooManyRequests = 429 // RFC 6585, 4
StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
StatusInternalServerError = 500 // RFC 7231, 6.6.1
StatusNotImplemented = 501 // RFC 7231, 6.6.2
StatusBadGateway = 502 // RFC 7231, 6.6.3
StatusServiceUnavailable = 503 // RFC 7231, 6.6.4
StatusGatewayTimeout = 504 // RFC 7231, 6.6.5
StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6
StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1
StatusInsufficientStorage = 507 // RFC 4918, 11.5
StatusLoopDetected = 508 // RFC 5842, 7.2
StatusNotExtended = 510 // RFC 2774, 7
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
StatusContinue = 100 // RFC 7231, 6.2.1
StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
StatusProcessing = 102 // RFC 2518, 10.1
StatusEarlyHints = 103 // RFC 8297
StatusOK = 200 // RFC 7231, 6.3.1
StatusCreated = 201 // RFC 7231, 6.3.2
StatusAccepted = 202 // RFC 7231, 6.3.3
StatusNonAuthoritativeInformation = 203 // RFC 7231, 6.3.4
StatusNoContent = 204 // RFC 7231, 6.3.5
StatusResetContent = 205 // RFC 7231, 6.3.6
StatusPartialContent = 206 // RFC 7233, 4.1
StatusMultiStatus = 207 // RFC 4918, 11.1
StatusAlreadyReported = 208 // RFC 5842, 7.1
StatusIMUsed = 226 // RFC 3229, 10.4.1
StatusMultipleChoices = 300 // RFC 7231, 6.4.1
StatusMovedPermanently = 301 // RFC 7231, 6.4.2
StatusFound = 302 // RFC 7231, 6.4.3
StatusSeeOther = 303 // RFC 7231, 6.4.4
StatusNotModified = 304 // RFC 7232, 4.1
StatusUseProxy = 305 // RFC 7231, 6.4.5
StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
StatusPermanentRedirect = 308 // RFC 7538, 3
StatusBadRequest = 400 // RFC 7231, 6.5.1
StatusUnauthorized = 401 // RFC 7235, 3.1
StatusPaymentRequired = 402 // RFC 7231, 6.5.2
StatusForbidden = 403 // RFC 7231, 6.5.3
StatusNotFound = 404 // RFC 7231, 6.5.4
StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5
StatusNotAcceptable = 406 // RFC 7231, 6.5.6
StatusProxyAuthRequired = 407 // RFC 7235, 3.2
StatusRequestTimeout = 408 // RFC 7231, 6.5.7
StatusConflict = 409 // RFC 7231, 6.5.8
StatusGone = 410 // RFC 7231, 6.5.9
StatusLengthRequired = 411 // RFC 7231, 6.5.10
StatusPreconditionFailed = 412 // RFC 7232, 4.2
StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11
StatusRequestURITooLong = 414 // RFC 7231, 6.5.12
StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13
StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
StatusExpectationFailed = 417 // RFC 7231, 6.5.14
StatusTeapot = 418 // RFC 7168, 2.3.3
StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2
StatusUnprocessableEntity = 422 // RFC 4918, 11.2
StatusLocked = 423 // RFC 4918, 11.3
StatusFailedDependency = 424 // RFC 4918, 11.4
StatusTooEarly = 425 // RFC 8470, 5.2.
StatusUpgradeRequired = 426 // RFC 7231, 6.5.15
StatusPreconditionRequired = 428 // RFC 6585, 3
StatusTooManyRequests = 429 // RFC 6585, 4
StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
StatusInternalServerError = 500 // RFC 7231, 6.6.1
StatusNotImplemented = 501 // RFC 7231, 6.6.2
StatusBadGateway = 502 // RFC 7231, 6.6.3
StatusServiceUnavailable = 503 // RFC 7231, 6.6.4
StatusGatewayTimeout = 504 // RFC 7231, 6.6.5
StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6
StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1
StatusInsufficientStorage = 507 // RFC 4918, 11.5
StatusLoopDetected = 508 // RFC 5842, 7.2
StatusNotExtended = 510 // RFC 2774, 7
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)
```
@ -121,46 +121,46 @@ const (
```go
var (
ErrBadRequest = NewError(StatusBadRequest) // RFC 7231, 6.5.1
ErrUnauthorized = NewError(StatusUnauthorized) // RFC 7235, 3.1
ErrPaymentRequired = NewError(StatusPaymentRequired) // RFC 7231, 6.5.2
ErrForbidden = NewError(StatusForbidden) // RFC 7231, 6.5.3
ErrNotFound = NewError(StatusNotFound) // RFC 7231, 6.5.4
ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // RFC 7231, 6.5.5
ErrNotAcceptable = NewError(StatusNotAcceptable) // RFC 7231, 6.5.6
ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // RFC 7235, 3.2
ErrRequestTimeout = NewError(StatusRequestTimeout) // RFC 7231, 6.5.7
ErrConflict = NewError(StatusConflict) // RFC 7231, 6.5.8
ErrGone = NewError(StatusGone) // RFC 7231, 6.5.9
ErrLengthRequired = NewError(StatusLengthRequired) // RFC 7231, 6.5.10
ErrPreconditionFailed = NewError(StatusPreconditionFailed) // RFC 7232, 4.2
ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // RFC 7231, 6.5.11
ErrRequestURITooLong = NewError(StatusRequestURITooLong) // RFC 7231, 6.5.12
ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // RFC 7231, 6.5.13
ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // RFC 7233, 4.4
ErrExpectationFailed = NewError(StatusExpectationFailed) // RFC 7231, 6.5.14
ErrTeapot = NewError(StatusTeapot) // RFC 7168, 2.3.3
ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // RFC 7540, 9.1.2
ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // RFC 4918, 11.2
ErrLocked = NewError(StatusLocked) // RFC 4918, 11.3
ErrFailedDependency = NewError(StatusFailedDependency) // RFC 4918, 11.4
ErrTooEarly = NewError(StatusTooEarly) // RFC 8470, 5.2.
ErrUpgradeRequired = NewError(StatusUpgradeRequired) // RFC 7231, 6.5.15
ErrPreconditionRequired = NewError(StatusPreconditionRequired) // RFC 6585, 3
ErrTooManyRequests = NewError(StatusTooManyRequests) // RFC 6585, 4
ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // RFC 6585, 5
ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // RFC 7725, 3
ErrInternalServerError = NewError(StatusInternalServerError) // RFC 7231, 6.6.1
ErrNotImplemented = NewError(StatusNotImplemented) // RFC 7231, 6.6.2
ErrBadGateway = NewError(StatusBadGateway) // RFC 7231, 6.6.3
ErrServiceUnavailable = NewError(StatusServiceUnavailable) // RFC 7231, 6.6.4
ErrGatewayTimeout = NewError(StatusGatewayTimeout) // RFC 7231, 6.6.5
ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // RFC 7231, 6.6.6
ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // RFC 2295, 8.1
ErrInsufficientStorage = NewError(StatusInsufficientStorage) // RFC 4918, 11.5
ErrLoopDetected = NewError(StatusLoopDetected) // RFC 5842, 7.2
ErrNotExtended = NewError(StatusNotExtended) // RFC 2774, 7
ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // RFC 6585, 6
ErrBadRequest = NewError(StatusBadRequest) // RFC 7231, 6.5.1
ErrUnauthorized = NewError(StatusUnauthorized) // RFC 7235, 3.1
ErrPaymentRequired = NewError(StatusPaymentRequired) // RFC 7231, 6.5.2
ErrForbidden = NewError(StatusForbidden) // RFC 7231, 6.5.3
ErrNotFound = NewError(StatusNotFound) // RFC 7231, 6.5.4
ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // RFC 7231, 6.5.5
ErrNotAcceptable = NewError(StatusNotAcceptable) // RFC 7231, 6.5.6
ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // RFC 7235, 3.2
ErrRequestTimeout = NewError(StatusRequestTimeout) // RFC 7231, 6.5.7
ErrConflict = NewError(StatusConflict) // RFC 7231, 6.5.8
ErrGone = NewError(StatusGone) // RFC 7231, 6.5.9
ErrLengthRequired = NewError(StatusLengthRequired) // RFC 7231, 6.5.10
ErrPreconditionFailed = NewError(StatusPreconditionFailed) // RFC 7232, 4.2
ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // RFC 7231, 6.5.11
ErrRequestURITooLong = NewError(StatusRequestURITooLong) // RFC 7231, 6.5.12
ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // RFC 7231, 6.5.13
ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // RFC 7233, 4.4
ErrExpectationFailed = NewError(StatusExpectationFailed) // RFC 7231, 6.5.14
ErrTeapot = NewError(StatusTeapot) // RFC 7168, 2.3.3
ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // RFC 7540, 9.1.2
ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // RFC 4918, 11.2
ErrLocked = NewError(StatusLocked) // RFC 4918, 11.3
ErrFailedDependency = NewError(StatusFailedDependency) // RFC 4918, 11.4
ErrTooEarly = NewError(StatusTooEarly) // RFC 8470, 5.2.
ErrUpgradeRequired = NewError(StatusUpgradeRequired) // RFC 7231, 6.5.15
ErrPreconditionRequired = NewError(StatusPreconditionRequired) // RFC 6585, 3
ErrTooManyRequests = NewError(StatusTooManyRequests) // RFC 6585, 4
ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // RFC 6585, 5
ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // RFC 7725, 3
ErrInternalServerError = NewError(StatusInternalServerError) // RFC 7231, 6.6.1
ErrNotImplemented = NewError(StatusNotImplemented) // RFC 7231, 6.6.2
ErrBadGateway = NewError(StatusBadGateway) // RFC 7231, 6.6.3
ErrServiceUnavailable = NewError(StatusServiceUnavailable) // RFC 7231, 6.6.4
ErrGatewayTimeout = NewError(StatusGatewayTimeout) // RFC 7231, 6.6.5
ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // RFC 7231, 6.6.6
ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // RFC 2295, 8.1
ErrInsufficientStorage = NewError(StatusInsufficientStorage) // RFC 4918, 11.5
ErrLoopDetected = NewError(StatusLoopDetected) // RFC 5842, 7.2
ErrNotExtended = NewError(StatusNotExtended) // RFC 2774, 7
ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // RFC 6585, 6
)
```
@ -168,127 +168,127 @@ HTTP Headers were copied from net/http.
```go
const (
HeaderAuthorization = "Authorization"
HeaderProxyAuthenticate = "Proxy-Authenticate"
HeaderProxyAuthorization = "Proxy-Authorization"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderAge = "Age"
HeaderCacheControl = "Cache-Control"
HeaderClearSiteData = "Clear-Site-Data"
HeaderExpires = "Expires"
HeaderPragma = "Pragma"
HeaderWarning = "Warning"
HeaderAcceptCH = "Accept-CH"
HeaderAcceptCHLifetime = "Accept-CH-Lifetime"
HeaderContentDPR = "Content-DPR"
HeaderDPR = "DPR"
HeaderEarlyData = "Early-Data"
HeaderSaveData = "Save-Data"
HeaderViewportWidth = "Viewport-Width"
HeaderWidth = "Width"
HeaderETag = "ETag"
HeaderIfMatch = "If-Match"
HeaderIfModifiedSince = "If-Modified-Since"
HeaderIfNoneMatch = "If-None-Match"
HeaderIfUnmodifiedSince = "If-Unmodified-Since"
HeaderLastModified = "Last-Modified"
HeaderVary = "Vary"
HeaderConnection = "Connection"
HeaderKeepAlive = "Keep-Alive"
HeaderAccept = "Accept"
HeaderAcceptCharset = "Accept-Charset"
HeaderAcceptEncoding = "Accept-Encoding"
HeaderAcceptLanguage = "Accept-Language"
HeaderCookie = "Cookie"
HeaderExpect = "Expect"
HeaderMaxForwards = "Max-Forwards"
HeaderSetCookie = "Set-Cookie"
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
HeaderOrigin = "Origin"
HeaderTimingAllowOrigin = "Timing-Allow-Origin"
HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
HeaderDNT = "DNT"
HeaderTk = "Tk"
HeaderContentDisposition = "Content-Disposition"
HeaderContentEncoding = "Content-Encoding"
HeaderContentLanguage = "Content-Language"
HeaderContentLength = "Content-Length"
HeaderContentLocation = "Content-Location"
HeaderContentType = "Content-Type"
HeaderForwarded = "Forwarded"
HeaderVia = "Via"
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXForwardedHost = "X-Forwarded-Host"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
HeaderXForwardedSsl = "X-Forwarded-Ssl"
HeaderXUrlScheme = "X-Url-Scheme"
HeaderLocation = "Location"
HeaderFrom = "From"
HeaderHost = "Host"
HeaderReferer = "Referer"
HeaderReferrerPolicy = "Referrer-Policy"
HeaderUserAgent = "User-Agent"
HeaderAllow = "Allow"
HeaderServer = "Server"
HeaderAcceptRanges = "Accept-Ranges"
HeaderContentRange = "Content-Range"
HeaderIfRange = "If-Range"
HeaderRange = "Range"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy"
HeaderExpectCT = "Expect-CT"
HeaderFeaturePolicy = "Feature-Policy"
HeaderPublicKeyPins = "Public-Key-Pins"
HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only"
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests"
HeaderXContentTypeOptions = "X-Content-Type-Options"
HeaderXDownloadOptions = "X-Download-Options"
HeaderXFrameOptions = "X-Frame-Options"
HeaderXPoweredBy = "X-Powered-By"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderLastEventID = "Last-Event-ID"
HeaderNEL = "NEL"
HeaderPingFrom = "Ping-From"
HeaderPingTo = "Ping-To"
HeaderReportTo = "Report-To"
HeaderTE = "TE"
HeaderTrailer = "Trailer"
HeaderTransferEncoding = "Transfer-Encoding"
HeaderSecWebSocketAccept = "Sec-WebSocket-Accept"
HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions"
HeaderSecWebSocketKey = "Sec-WebSocket-Key"
HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol"
HeaderSecWebSocketVersion = "Sec-WebSocket-Version"
HeaderAcceptPatch = "Accept-Patch"
HeaderAcceptPushPolicy = "Accept-Push-Policy"
HeaderAcceptSignature = "Accept-Signature"
HeaderAltSvc = "Alt-Svc"
HeaderDate = "Date"
HeaderIndex = "Index"
HeaderLargeAllocation = "Large-Allocation"
HeaderLink = "Link"
HeaderPushPolicy = "Push-Policy"
HeaderRetryAfter = "Retry-After"
HeaderServerTiming = "Server-Timing"
HeaderSignature = "Signature"
HeaderSignedHeaders = "Signed-Headers"
HeaderSourceMap = "SourceMap"
HeaderUpgrade = "Upgrade"
HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
HeaderXPingback = "X-Pingback"
HeaderXRequestID = "X-Request-ID"
HeaderXRequestedWith = "X-Requested-With"
HeaderXRobotsTag = "X-Robots-Tag"
HeaderXUACompatible = "X-UA-Compatible"
HeaderAuthorization = "Authorization"
HeaderProxyAuthenticate = "Proxy-Authenticate"
HeaderProxyAuthorization = "Proxy-Authorization"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderAge = "Age"
HeaderCacheControl = "Cache-Control"
HeaderClearSiteData = "Clear-Site-Data"
HeaderExpires = "Expires"
HeaderPragma = "Pragma"
HeaderWarning = "Warning"
HeaderAcceptCH = "Accept-CH"
HeaderAcceptCHLifetime = "Accept-CH-Lifetime"
HeaderContentDPR = "Content-DPR"
HeaderDPR = "DPR"
HeaderEarlyData = "Early-Data"
HeaderSaveData = "Save-Data"
HeaderViewportWidth = "Viewport-Width"
HeaderWidth = "Width"
HeaderETag = "ETag"
HeaderIfMatch = "If-Match"
HeaderIfModifiedSince = "If-Modified-Since"
HeaderIfNoneMatch = "If-None-Match"
HeaderIfUnmodifiedSince = "If-Unmodified-Since"
HeaderLastModified = "Last-Modified"
HeaderVary = "Vary"
HeaderConnection = "Connection"
HeaderKeepAlive = "Keep-Alive"
HeaderAccept = "Accept"
HeaderAcceptCharset = "Accept-Charset"
HeaderAcceptEncoding = "Accept-Encoding"
HeaderAcceptLanguage = "Accept-Language"
HeaderCookie = "Cookie"
HeaderExpect = "Expect"
HeaderMaxForwards = "Max-Forwards"
HeaderSetCookie = "Set-Cookie"
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
HeaderOrigin = "Origin"
HeaderTimingAllowOrigin = "Timing-Allow-Origin"
HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
HeaderDNT = "DNT"
HeaderTk = "Tk"
HeaderContentDisposition = "Content-Disposition"
HeaderContentEncoding = "Content-Encoding"
HeaderContentLanguage = "Content-Language"
HeaderContentLength = "Content-Length"
HeaderContentLocation = "Content-Location"
HeaderContentType = "Content-Type"
HeaderForwarded = "Forwarded"
HeaderVia = "Via"
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXForwardedHost = "X-Forwarded-Host"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
HeaderXForwardedSsl = "X-Forwarded-Ssl"
HeaderXUrlScheme = "X-Url-Scheme"
HeaderLocation = "Location"
HeaderFrom = "From"
HeaderHost = "Host"
HeaderReferer = "Referer"
HeaderReferrerPolicy = "Referrer-Policy"
HeaderUserAgent = "User-Agent"
HeaderAllow = "Allow"
HeaderServer = "Server"
HeaderAcceptRanges = "Accept-Ranges"
HeaderContentRange = "Content-Range"
HeaderIfRange = "If-Range"
HeaderRange = "Range"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy"
HeaderExpectCT = "Expect-CT"
HeaderFeaturePolicy = "Feature-Policy"
HeaderPublicKeyPins = "Public-Key-Pins"
HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only"
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests"
HeaderXContentTypeOptions = "X-Content-Type-Options"
HeaderXDownloadOptions = "X-Download-Options"
HeaderXFrameOptions = "X-Frame-Options"
HeaderXPoweredBy = "X-Powered-By"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderLastEventID = "Last-Event-ID"
HeaderNEL = "NEL"
HeaderPingFrom = "Ping-From"
HeaderPingTo = "Ping-To"
HeaderReportTo = "Report-To"
HeaderTE = "TE"
HeaderTrailer = "Trailer"
HeaderTransferEncoding = "Transfer-Encoding"
HeaderSecWebSocketAccept = "Sec-WebSocket-Accept"
HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions"
HeaderSecWebSocketKey = "Sec-WebSocket-Key"
HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol"
HeaderSecWebSocketVersion = "Sec-WebSocket-Version"
HeaderAcceptPatch = "Accept-Patch"
HeaderAcceptPushPolicy = "Accept-Push-Policy"
HeaderAcceptSignature = "Accept-Signature"
HeaderAltSvc = "Alt-Svc"
HeaderDate = "Date"
HeaderIndex = "Index"
HeaderLargeAllocation = "Large-Allocation"
HeaderLink = "Link"
HeaderPushPolicy = "Push-Policy"
HeaderRetryAfter = "Retry-After"
HeaderServerTiming = "Server-Timing"
HeaderSignature = "Signature"
HeaderSignedHeaders = "Signed-Headers"
HeaderSourceMap = "SourceMap"
HeaderUpgrade = "Upgrade"
HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
HeaderXPingback = "X-Pingback"
HeaderXRequestID = "X-Request-ID"
HeaderXRequestedWith = "X-Requested-With"
HeaderXRobotsTag = "X-Robots-Tag"
HeaderXUACompatible = "X-UA-Compatible"
)
```

View File

@ -165,7 +165,6 @@ Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US
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.
:::
@ -375,17 +374,17 @@ func (c Ctx) Cookie(cookie *Cookie)
```go
type Cookie struct {
Name string `json:"name"` // The name of the cookie
Value string `json:"value"` // The value of the cookie
Path string `json:"path"` // Specifies a URL path which is allowed to receive the cookie
Domain string `json:"domain"` // Specifies the domain which is allowed to receive the cookie
MaxAge int `json:"max_age"` // The maximum age (in seconds) of the cookie
Expires time.Time `json:"expires"` // The expiration date of the cookie
Secure bool `json:"secure"` // Indicates that the cookie should only be transmitted over a secure HTTPS connection
HTTPOnly bool `json:"http_only"` // Indicates that the cookie is accessible only through the HTTP protocol
SameSite string `json:"same_site"` // Controls whether or not a cookie is sent with cross-site requests
Partitioned bool `json:"partitioned"` // Indicates if the cookie is stored in a partitioned cookie jar
SessionOnly bool `json:"session_only"` // Indicates if the cookie is a session-only cookie
Name string `json:"name"` // The name of the cookie
Value string `json:"value"` // The value of the cookie
Path string `json:"path"` // Specifies a URL path which is allowed to receive the cookie
Domain string `json:"domain"` // Specifies the domain which is allowed to receive the cookie
MaxAge int `json:"max_age"` // The maximum age (in seconds) of the cookie
Expires time.Time `json:"expires"` // The expiration date of the cookie
Secure bool `json:"secure"` // Indicates that the cookie should only be transmitted over a secure HTTPS connection
HTTPOnly bool `json:"http_only"` // Indicates that the cookie is accessible only through the HTTP protocol
SameSite string `json:"same_site"` // Controls whether or not a cookie is sent with cross-site requests
Partitioned bool `json:"partitioned"` // Indicates if the cookie is stored in a partitioned cookie jar
SessionOnly bool `json:"session_only"` // Indicates if the cookie is a session-only cookie
}
```
@ -1001,7 +1000,7 @@ 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
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"
@ -1023,7 +1022,7 @@ app.Get("/test", func(c Ctx) error {
})
````
Make sure to understand and correctly implement the Locals method in both its standard and generic form for better control
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
@ -1206,9 +1205,8 @@ Make copies or use the [**`Immutable`**](./ctx.md) setting instead. [Read more..
:::
In certain scenarios, it can be useful to have an alternative approach to handle different types of parameters, not
just strings. This can be achieved using a generic Query function known as `Params[V GenericType](c Ctx, key string, defaultValue ...V) V`.
In certain scenarios, it can be useful to have an alternative approach to handle different types of parameters, not
just strings. This can be achieved using a generic Query function known as `Params[V GenericType](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 GenericType`.
```go title="Signature"
@ -1227,6 +1225,7 @@ app.Get("/user/:id", func(c fiber.Ctx) error{
```
The generic Params function supports returning the following data types based on V GenericType:
- Integer: int, int8, int16, int32, int64
- Unsigned integer: uint, uint8, uint16, uint32, uint64
- Floating-point numbers: float32, float64
@ -1234,7 +1233,6 @@ The generic Params function supports returning the following data types based on
- String: string
- Byte array: []byte
## Path
Contains the path part of the request URL. Optionally, you could override the path by passing a string. For internal redirects, you might want to call [RestartRouting](ctx.md#restartrouting) instead of [Next](ctx.md#next).
@ -1303,11 +1301,11 @@ func (c Ctx) Queries() map[string]string
// GET http://example.com/?name=alex&want_pizza=false&id=
app.Get("/", func(c fiber.Ctx) error {
m := c.Queries()
m["name"] // "alex"
m["want_pizza"] // "false"
m["id"] // ""
// ...
m := c.Queries()
m["name"] // "alex"
m["want_pizza"] // "false"
m["id"] // ""
// ...
})
```
@ -1315,9 +1313,9 @@ app.Get("/", func(c fiber.Ctx) error {
// GET http://example.com/?field1=value1&field1=value2&field2=value3
app.Get("/", func (c fiber.Ctx) error {
m := c.Queries()
m["field1"] // "value2"
m["field2"] // value3
m := c.Queries()
m["field1"] // "value2"
m["field2"] // value3
})
```
@ -1325,10 +1323,10 @@ app.Get("/", func (c fiber.Ctx) error {
// 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 {
m := c.Queries()
m["list_a"] // "3"
m["list_b[]"] // "3"
m["list_c"] // "1,2,3"
m := c.Queries()
m["list_a"] // "3"
m["list_b[]"] // "3"
m["list_c"] // "1,2,3"
})
```
@ -1336,9 +1334,9 @@ app.Get("/", func(c fiber.Ctx) error {
// GET /api/posts?filters.author.name=John&filters.category.name=Technology
app.Get("/", func(c fiber.Ctx) error {
m := c.Queries()
m["filters.author.name"] // John
m["filters.category.name"] // Technology
m := c.Queries()
m["filters.author.name"] // John
m["filters.category.name"] // Technology
})
```
@ -1346,12 +1344,12 @@ app.Get("/", func(c fiber.Ctx) error {
// 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 {
m := c.Queries()
m["tags"] // apple,orange,banana
m["filters[tags]"] // apple,orange,banana
m["filters[category][name]"] // fruits
m["filters.tags"] // apple,orange,banana
m["filters.category.name"] // fruits
m := c.Queries()
m["tags"] // apple,orange,banana
m["filters[tags]"] // apple,orange,banana
m["filters[category][name]"] // fruits
m["filters.tags"] // apple,orange,banana
m["filters.category.name"] // fruits
})
```
@ -1386,8 +1384,8 @@ Make copies or use the [**`Immutable`**](./ctx.md) setting instead. [Read more..
:::
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 GenericType](c Ctx, key string, defaultValue ...V) V`.
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 GenericType](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 GenericType`.
Here is the signature for the generic Query function:
@ -1410,12 +1408,13 @@ app.Get("/", func(c fiber.Ctx) error {
})
```
In this case, `Query[V GenericType](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
In this case, `Query[V GenericType](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 GenericType:
- Integer: int, int8, int16, int32, int64
- Unsigned integer: uint, uint8, uint16, uint32, uint64
- Floating-point numbers: float32, float64
@ -1464,7 +1463,6 @@ app.Get("/teapot", func(c fiber.Ctx) error {
})
```
## Render
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).
@ -1746,7 +1744,7 @@ type SendFile struct {
// The value for the Cache-Control HTTP-header
// that is set on the file response. MaxAge is defined in seconds.
//
// Optional. Default value 0.
// Optional. Default value 0.
MaxAge int `json:"max_age"`
}
```

View File

@ -82,7 +82,6 @@ app := fiber.New(fiber.Config{
| <Reference id="writetimeout">WriteTimeout</Reference> | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `nil` |
| <Reference id="xmlencoder">XMLEncoder</Reference> | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` |
## Server listening
### Config
@ -112,10 +111,9 @@ app.Listen(":8080", fiber.ListenConfig{
| <Reference id="listeneraddrfunc">ListenerAddrFunc</Reference> | `func(addr net.Addr)` | Allows accessing and customizing `net.Listener`. | `nil` |
| <Reference id="listenernetwork">ListenerNetwork</Reference> | `string` | Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only). WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chosen. | `tcp4` |
| <Reference id="onshutdownerror">OnShutdownError</Reference> | `func(err error)` | Allows to customize error behavior when gracefully shutting down the server by given signal. Prints error with `log.Fatalf()` | `nil` |
| <Reference id="onshutdownsuccess">OnShutdownSuccess</Reference> | `func()` | Allows to customize success behavior when gracefully shutting down the server by given signal. | `nil` |
| <Reference id="onshutdownsuccess">OnShutdownSuccess</Reference> | `func()` | Allows customizing success behavior when gracefully shutting down the server by given signal. | `nil` |
| <Reference id="tlsconfigfunc">TLSConfigFunc</Reference> | `func(tlsConfig *tls.Config)` | Allows customizing `tls.Config` as you want. | `nil` |
### Listen
Listen serves HTTP requests from the given address.
@ -215,7 +213,6 @@ func (app *App) ShutdownWithTimeout(timeout time.Duration) error
func (app *App) ShutdownWithContext(ctx context.Context) error
```
## Helper functions
### NewError

View File

@ -7,7 +7,8 @@ sidebar_position: 7
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
With Fiber v2.30.0, you can execute custom user functions when to run some methods. Here is a list of this hooks:
With Fiber v2.30.0, you can execute custom user functions when to run some methods. Here is a list of these hooks:
- [OnRoute](#onroute)
- [OnName](#onname)
- [OnGroup](#ongroup)
@ -18,6 +19,7 @@ With Fiber v2.30.0, you can execute custom user functions when to run some metho
- [OnMount](#onmount)
## Constants
```go
// Handlers define a function to create hooks for Fiber.
type OnRouteHandler = func(Route) error
@ -57,45 +59,46 @@ func (h *Hooks) OnName(handler ...OnNameHandler)
package main
import (
"fmt"
"fmt"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
return c.SendString(c.Route().Name)
}).Name("index")
app.Get("/", func(c fiber.Ctx) error {
return c.SendString(c.Route().Name)
}).Name("index")
app.Hooks().OnName(func(r fiber.Route) error {
fmt.Print("Name: " + r.Name + ", ")
app.Hooks().OnName(func(r fiber.Route) error {
fmt.Print("Name: " + r.Name + ", ")
return nil
})
return nil
})
app.Hooks().OnName(func(r fiber.Route) error {
fmt.Print("Method: " + r.Method + "\n")
app.Hooks().OnName(func(r fiber.Route) error {
fmt.Print("Method: " + r.Method + "\n")
return nil
})
return nil
})
app.Get("/add/user", func(c fiber.Ctx) error {
return c.SendString(c.Route().Name)
}).Name("addUser")
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 {
return c.SendString(c.Route().Name)
}).Name("destroyUser")
app.Delete("/destroy/user", func(c fiber.Ctx) error {
return c.SendString(c.Route().Name)
}).Name("destroyUser")
app.Listen(":5000")
app.Listen(":5000")
}
// Results:
// Name: addUser, Method: GET
// Name: destroyUser, Method: DELETE
```
</TabItem>
</Tabs>
@ -137,7 +140,7 @@ app := fiber.New(fiber.Config{
app.Hooks().OnListen(func(listenData fiber.ListenData) error {
if fiber.IsChild() {
return nil
return nil
}
scheme := "http"
if data.TLS {
@ -184,26 +187,26 @@ func (h *Hooks) OnMount(handler ...OnMountHandler)
package main
import (
"fmt"
"fmt"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3"
)
func main() {
app := New()
app.Get("/", testSimpleHandler).Name("x")
app := New()
app.Get("/", testSimpleHandler).Name("x")
subApp := New()
subApp.Get("/test", testSimpleHandler)
subApp := New()
subApp.Get("/test", testSimpleHandler)
subApp.Hooks().OnMount(func(parent *fiber.App) error {
fmt.Print("Mount path of parent app: "+parent.MountPath())
// ...
subApp.Hooks().OnMount(func(parent *fiber.App) error {
fmt.Print("Mount path of parent app: "+parent.MountPath())
// ...
return nil
})
return nil
})
app.Mount("/sub", subApp)
app.Mount("/sub", subApp)
}
// Result:
@ -213,6 +216,5 @@ func main() {
</TabItem>
</Tabs>
:::caution
OnName/OnRoute/OnGroup/OnGroupName hooks are mount-sensitive. If you use one of these routes on sub app and you mount it; paths of routes and groups will start with mount prefix.
OnName/OnRoute/OnGroup/OnGroupName hooks are mount-sensitive. If you use one of these routes on sub app, and you mount it; paths of routes and groups will start with mount prefix.

View File

@ -13,13 +13,13 @@ Fiber offers a default mechanism for logging to standard output. Additionally, i
```go
const (
LevelTrace Level = iota
LevelDebug
LevelInfo
LevelWarn
LevelError
LevelFatal
LevelPanic
LevelTrace Level = iota
LevelDebug
LevelInfo
LevelWarn
LevelError
LevelFatal
LevelPanic
)
```
@ -42,9 +42,11 @@ type AllLogger interface {
```
## Print log
Note: The Fatal level method will terminate the program after printing the log message. Please use it with caution.
### Basic Logging
Logs of different levels can be directly printed. These will be entered into `messageKey`, with the default key being `msg`.
```go
@ -58,6 +60,7 @@ log.Panic("The system is down.")
```
### Formatted Logging
Logs of different levels can be formatted before printing. All such methods end with an `f`.
```go
@ -69,6 +72,7 @@ log.Fatalf("So Long, and Thanks for All the %s.", "banana")
```
### Key-Value Logging
Print a message with key-value pairs. If the key and value are not paired correctly, the log will output `KEYVALS UNPAIRED`.
```go
@ -80,6 +84,7 @@ log.Fatalw("", "fruit", "banana")
```
## Global log
For projects that require a simple, global logging function to print messages at any time, Fiber provides a global log.
```go
@ -113,6 +118,7 @@ fiberlog.SetLogger(customLogger)
```
## Set Level
`log.SetLevel` sets the minimum level of logs that will be output. The default log level is `LevelTrace`.
Note that this method is not **concurrent-safe**.

View File

@ -190,7 +190,6 @@ app.Get("/", func(c fiber.Ctx) error {
})
```
#### OldInputs
Get old input data. Check [WithInput](#withinput) for more information.

View File

@ -18,28 +18,29 @@ import TabItem from '@theme/TabItem';
package main
import (
"encoding/base64"
"fmt"
"encoding/base64"
"fmt"
"github.com/gofiber/fiber/v3/client"
"github.com/gofiber/fiber/v3/client"
)
func main() {
cc := client.New()
cc := client.New()
out := base64.StdEncoding.EncodeToString([]byte("john:doe"))
resp, err := cc.Get("http://localhost:3000", client.Config{
Header: map[string]string{
"Authorization": "Basic " + out,
},
})
if err != nil {
panic(err)
}
out := base64.StdEncoding.EncodeToString([]byte("john:doe"))
resp, err := cc.Get("http://localhost:3000", client.Config{
Header: map[string]string{
"Authorization": "Basic " + out,
},
})
if err != nil {
panic(err)
}
fmt.Print(string(resp.Body()))
fmt.Print(string(resp.Body()))
}
```
</TabItem>
<TabItem value="server" label="Server">
@ -47,27 +48,28 @@ func main() {
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/basicauth"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/basicauth"
)
func main() {
app := fiber.New()
app.Use(
basicauth.New(basicauth.Config{
Users: map[string]string{
"john": "doe",
},
}),
)
app := fiber.New()
app.Use(
basicauth.New(basicauth.Config{
Users: map[string]string{
"john": "doe",
},
}),
)
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
app.Listen(":3000")
}
```
</TabItem>
</Tabs>
@ -80,40 +82,41 @@ func main() {
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"github.com/gofiber/fiber/v3/client"
"github.com/gofiber/fiber/v3/client"
)
func main() {
cc := client.New()
cc := client.New()
certPool, err := x509.SystemCertPool()
if err != nil {
panic(err)
}
certPool, err := x509.SystemCertPool()
if err != nil {
panic(err)
}
cert, err := os.ReadFile("ssl.cert")
if err != nil {
panic(err)
}
cert, err := os.ReadFile("ssl.cert")
if err != nil {
panic(err)
}
certPool.AppendCertsFromPEM(cert)
cc.SetTLSConfig(&tls.Config{
RootCAs: certPool,
})
certPool.AppendCertsFromPEM(cert)
cc.SetTLSConfig(&tls.Config{
RootCAs: certPool,
})
resp, err := cc.Get("https://localhost:3000")
if err != nil {
panic(err)
}
resp, err := cc.Get("https://localhost:3000")
if err != nil {
panic(err)
}
fmt.Print(string(resp.Body()))
fmt.Print(string(resp.Body()))
}
```
</TabItem>
<TabItem value="server" label="Server">
@ -121,25 +124,26 @@ func main() {
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3"
)
func main() {
app := fiber.New()
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
err := app.Listen(":3000", fiber.ListenConfig{
CertFile: "ssl.cert",
CertKeyFile: "ssl.key",
})
if err != nil {
panic(err)
}
err := app.Listen(":3000", fiber.ListenConfig{
CertFile: "ssl.cert",
CertKeyFile: "ssl.key",
})
if err != nil {
panic(err)
}
}
```
</TabItem>
</Tabs>
@ -149,20 +153,20 @@ func main() {
```go
func main() {
jar := client.AcquireCookieJar()
defer client.ReleaseCookieJar(jar)
jar := client.AcquireCookieJar()
defer client.ReleaseCookieJar(jar)
cc := client.New()
cc.SetCookieJar(jar)
cc := client.New()
cc.SetCookieJar(jar)
jar.SetKeyValueBytes("httpbin.org", []byte("john"), []byte("doe"))
jar.SetKeyValueBytes("httpbin.org", []byte("john"), []byte("doe"))
resp, err := cc.Get("https://httpbin.org/cookies")
if err != nil {
panic(err)
}
resp, err := cc.Get("https://httpbin.org/cookies")
if err != nil {
panic(err)
}
fmt.Println(string(resp.Body()))
fmt.Println(string(resp.Body()))
}
```
@ -183,23 +187,23 @@ func main() {
```go
func main() {
jar := client.AcquireCookieJar()
defer client.ReleaseCookieJar(jar)
jar := client.AcquireCookieJar()
defer client.ReleaseCookieJar(jar)
cc := client.New()
cc.SetCookieJar(jar)
cc := client.New()
cc.SetCookieJar(jar)
_, err := cc.Get("https://httpbin.org/cookies/set/john/doe")
if err != nil {
panic(err)
}
_, err := cc.Get("https://httpbin.org/cookies/set/john/doe")
if err != nil {
panic(err)
}
uri := fasthttp.AcquireURI()
defer fasthttp.ReleaseURI(uri)
uri := fasthttp.AcquireURI()
defer fasthttp.ReleaseURI(uri)
uri.SetHost("httpbin.org")
uri.SetPath("/cookies")
fmt.Println(jar.Get(uri))
uri.SetHost("httpbin.org")
uri.SetPath("/cookies")
fmt.Println(jar.Get(uri))
}
```
@ -216,23 +220,23 @@ func main() {
```go
func main() {
jar := client.AcquireCookieJar()
defer client.ReleaseCookieJar(jar)
jar := client.AcquireCookieJar()
defer client.ReleaseCookieJar(jar)
cc := client.New()
cc.SetCookieJar(jar)
cc := client.New()
cc.SetCookieJar(jar)
_, err := cc.Get("https://httpbin.org/cookies/set/john/doe")
if err != nil {
panic(err)
}
_, err := cc.Get("https://httpbin.org/cookies/set/john/doe")
if err != nil {
panic(err)
}
resp, err := cc.Get("https://httpbin.org/cookies")
if err != nil {
panic(err)
}
resp, err := cc.Get("https://httpbin.org/cookies")
if err != nil {
panic(err)
}
fmt.Println(resp.String())
fmt.Println(resp.String())
}
```

View File

@ -18,43 +18,43 @@ You need to use `RequestHook func(*Client, *Request) error` function signature w
```go
type Repository struct {
Name string `json:"name"`
FullName string `json:"full_name"`
Description string `json:"description"`
Homepage string `json:"homepage"`
Name string `json:"name"`
FullName string `json:"full_name"`
Description string `json:"description"`
Homepage string `json:"homepage"`
Owner struct {
Login string `json:"login"`
} `json:"owner"`
Owner struct {
Login string `json:"login"`
} `json:"owner"`
}
func main() {
cc := client.New()
cc := client.New()
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
r.SetURL("https://api.github.com/" + r.URL())
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
r.SetURL("https://api.github.com/" + r.URL())
return nil
})
return nil
})
resp, err := cc.Get("repos/gofiber/fiber")
if err != nil {
panic(err)
}
resp, err := cc.Get("repos/gofiber/fiber")
if err != nil {
panic(err)
}
var repo Repository
if err := resp.JSON(&repo); err != nil {
panic(err)
}
var repo Repository
if err := resp.JSON(&repo); err != nil {
panic(err)
}
fmt.Printf("Status code: %d\n", resp.StatusCode())
fmt.Printf("Status code: %d\n", resp.StatusCode())
fmt.Printf("Repository: %s\n", repo.FullName)
fmt.Printf("Description: %s\n", repo.Description)
fmt.Printf("Homepage: %s\n", repo.Homepage)
fmt.Printf("Owner: %s\n", repo.Owner.Login)
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Full Name: %s\n", repo.FullName)
fmt.Printf("Repository: %s\n", repo.FullName)
fmt.Printf("Description: %s\n", repo.Description)
fmt.Printf("Homepage: %s\n", repo.Homepage)
fmt.Printf("Owner: %s\n", repo.Owner.Login)
fmt.Printf("Name: %s\n", repo.Name)
fmt.Printf("Full Name: %s\n", repo.FullName)
}
```
@ -87,22 +87,22 @@ If any error returns from request hook execution, it will interrupt the request
```go
func main() {
cc := client.New()
cc := client.New()
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 1")
return errors.New("error")
})
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 1")
return errors.New("error")
})
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 2")
return nil
})
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 2")
return nil
})
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
```
@ -129,24 +129,24 @@ You need to use `ResponseHook func(*Client, *Response, *Request) error` function
```go
func main() {
cc := client.New()
cc := client.New()
cc.AddResponseHook(func(c *client.Client, resp *client.Response, req *client.Request) error {
fmt.Printf("Response Status Code: %d\n", resp.StatusCode())
fmt.Printf("HTTP protocol: %s\n\n", resp.Protocol())
cc.AddResponseHook(func(c *client.Client, resp *client.Response, req *client.Request) error {
fmt.Printf("Response Status Code: %d\n", resp.StatusCode())
fmt.Printf("HTTP protocol: %s\n\n", resp.Protocol())
fmt.Println("Response Headers:")
resp.RawResponse.Header.VisitAll(func(key, value []byte) {
fmt.Printf("%s: %s\n", key, value)
})
fmt.Println("Response Headers:")
resp.RawResponse.Header.VisitAll(func(key, value []byte) {
fmt.Printf("%s: %s\n", key, value)
})
return nil
})
return nil
})
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
```
@ -185,27 +185,27 @@ If any error is returned from executing the response hook, it will return the er
```go
func main() {
cc := client.New()
cc := client.New()
cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 1")
return nil
})
cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 1")
return nil
})
cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 2")
return errors.New("error")
})
cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 2")
return errors.New("error")
})
cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 3")
return nil
})
cc.AddResponseHook(func(c *client.Client, r1 *client.Response, r2 *client.Request) error {
fmt.Println("Hook 3")
return nil
})
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
```
@ -231,22 +231,22 @@ Hooks work as FIFO (first-in-first-out). You need to check the order while addin
```go
func main() {
cc := client.New()
cc := client.New()
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 1")
return nil
})
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 1")
return nil
})
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 2")
return nil
})
cc.AddRequestHook(func(c *client.Client, r *client.Request) error {
fmt.Println("Hook 2")
return nil
})
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
_, err := cc.Get("https://example.com/")
if err != nil {
panic(err)
}
}
```

View File

@ -18,32 +18,31 @@ This structure is designed to be flexible and efficient, allowing users to easil
```go
type Request struct {
url string
method string
userAgent string
boundary string
referer string
ctx context.Context
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
url string
method string
userAgent string
boundary string
referer string
ctx context.Context
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
timeout time.Duration
maxRedirects int
timeout time.Duration
maxRedirects int
client *Client
client *Client
body any
formData *FormData
files []*File
bodyType bodyType
body any
formData *FormData
files []*File
bodyType bodyType
RawRequest *fasthttp.Request
RawRequest *fasthttp.Request
}
```
## REST Methods
### Get
@ -207,6 +206,7 @@ func (r *Request) SetContext(ctx context.Context) *Request
## Header
Header method returns header value via key, this method will visit all field in the header.
```go title="Signature"
func (r *Request) Header(key string) []string
```
@ -229,7 +229,7 @@ req.AddHeader("Test", "654321")
resp, err := req.Get("https://httpbin.org/headers")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(resp.String())
@ -271,7 +271,7 @@ req.SetHeader("Test", "654321")
resp, err := req.Get("https://httpbin.org/headers")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(resp.String())
@ -338,7 +338,7 @@ req.AddParam("hobbies", "basketball")
resp, err := req.Get("https://httpbin.org/response-headers")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -401,19 +401,19 @@ req := client.AcquireRequest()
defer client.ReleaseRequest(req)
req.SetParamsWithStruct(struct {
Name string `json:"name"`
Hobbies []string `json:"hobbies"`
Name string `json:"name"`
Hobbies []string `json:"hobbies"`
}{
Name: "John Doe",
Hobbies: []string{
"Football",
"Basketball",
},
Name: "John Doe",
Hobbies: []string{
"Football",
"Basketball",
},
})
resp, err := req.Get("https://httpbin.org/response-headers")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -525,13 +525,13 @@ req := client.AcquireRequest()
defer client.ReleaseRequest(req)
req.SetCookies(map[string]string{
"cookie1": "value1",
"cookie2": "value2",
"cookie1": "value1",
"cookie2": "value2",
})
resp, err := req.Get("https://httpbin.org/cookies")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -592,7 +592,7 @@ req.SetPathParam("base64", "R29maWJlcg==")
resp, err := req.Get("https://httpbin.org/base64/:base64")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -691,7 +691,7 @@ req.AddFormData("points", "100")
resp, err := req.Post("https://httpbin.org/post")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -735,7 +735,7 @@ req.SetFormData("email", "john@doe.com")
resp, err := req.Post("https://httpbin.org/post")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -832,7 +832,7 @@ req.AddFile("test.txt")
resp, err := req.Post("https://httpbin.org/post")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -872,7 +872,7 @@ req.AddFileWithReader("test.txt", io.NopCloser(buf))
resp, err := req.Post("https://httpbin.org/post")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -928,7 +928,7 @@ req.SetTimeout(5 * time.Second)
resp, err := req.Get("https://httpbin.org/delay/4")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -957,7 +957,7 @@ req.SetTimeout(5 * time.Second)
resp, err := req.Get("https://httpbin.org/delay/6")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -1016,7 +1016,7 @@ Header is a wrapper which wrap http.Header, the header in client and request wil
```go
type Header struct {
*fasthttp.RequestHeader
*fasthttp.RequestHeader
}
```
@ -1050,7 +1050,7 @@ QueryParam is a wrapper which wrap url.Values, the query string and formdata in
```go
type QueryParam struct {
*fasthttp.Args
*fasthttp.Args
}
```
@ -1229,7 +1229,7 @@ FormData is a wrapper of fasthttp.Args and it is used for url encode body and fi
```go
type FormData struct {
*fasthttp.Args
*fasthttp.Args
}
```
@ -1295,10 +1295,10 @@ File is a struct which support send files via request.
```go
type File struct {
name string
fieldName string
path string
reader io.ReadCloser
name string
fieldName string
path string
reader io.ReadCloser
}
```

View File

@ -17,11 +17,11 @@ This structure allows users to easily access and manage the data returned by the
```go
type Response struct {
client *Client
request *Request
cookie []*fasthttp.Cookie
client *Client
request *Request
cookie []*fasthttp.Cookie
RawResponse *fasthttp.Response
RawResponse *fasthttp.Response
}
```
@ -71,7 +71,7 @@ func (r *Response) Protocol() string
```go title="Example"
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(resp.Protocol())
@ -80,7 +80,7 @@ fmt.Println(resp.Protocol())
<details>
<summary>Click here to see the result</summary>
```
```text
HTTP/1.1
```
@ -105,19 +105,19 @@ func (r *Response) Cookies() []*fasthttp.Cookie
```go title="Example"
resp, err := client.Get("https://httpbin.org/cookies/set/go/fiber")
if err != nil {
panic(err)
panic(err)
}
cookies := resp.Cookies()
for _, cookie := range cookies {
fmt.Printf("%s => %s\n", string(cookie.Key()), string(cookie.Value()))
fmt.Printf("%s => %s\n", string(cookie.Key()), string(cookie.Value()))
}
```
<details>
<summary>Click here to see the result</summary>
```
```text
go => fiber
```
@ -149,22 +149,22 @@ func (r *Response) JSON(v any) error
```go title="Example"
type Body struct {
Slideshow struct {
Author string `json:"author"`
Date string `json:"date"`
Title string `json:"title"`
} `json:"slideshow"`
Slideshow struct {
Author string `json:"author"`
Date string `json:"date"`
Title string `json:"title"`
} `json:"slideshow"`
}
var out Body
resp, err := client.Get("https://httpbin.org/json")
if err != nil {
panic(err)
panic(err)
}
err = resp.JSON(&out)
if err != nil {
panic(err)
panic(err)
}
fmt.Printf("%+v\n", out)
@ -173,7 +173,7 @@ fmt.Printf("%+v\n", out)
<details>
<summary>Click here to see the result</summary>
```
```text
{Slideshow:{Author:Yours Truly Date:date of publication Title:Sample Slide Show}}
```

View File

@ -10,36 +10,38 @@ toc_max_heading_level: 5
The Fiber Client for Fiber v3 is a powerful HTTP client optimized for high performance and ease of use in server-side applications. Built on top of the robust FastHTTP library, it inherits FastHTTP's high-speed HTTP protocol implementation. The client is designed to make HTTP requests both internally within services or externally to other web services.
## Features
- **Lightweight & Fast**: Leveraging the minimalistic design of FastHTTP, the Fiber Client is lightweight and extremely fast.
- **Flexible Configuration**: Configure client-level settings such as timeouts, headers, and more, which apply to all requests. Specific requests can further override or merge these settings.
- **Connection Pooling**: Manages a pool of persistent connections that reduce the overhead of repeatedly establishing connections.
- **Timeouts & Retries**: Supports setting request timeouts and retry mechanisms to handle transient failures.
## Usage
To use the Fiber Client, instantiate it with the desired configuration. Here's a simple example:
```go
package main
import (
"fmt"
"time"
"fmt"
"time"
"github.com/gofiber/fiber/v3/client"
"github.com/gofiber/fiber/v3/client"
)
func main() {
cc := client.New()
cc.SetTimeout(10 * time.Second)
cc := client.New()
cc.SetTimeout(10 * time.Second)
// Get request
resp, err := cc.Get("https://httpbin.org/get")
if err != nil {
panic(err)
}
// Get request
resp, err := cc.Get("https://httpbin.org/get")
if err != nil {
panic(err)
}
fmt.Printf("Status: %d\n", resp.StatusCode())
fmt.Printf("Body: %s\n", string(resp.Body()))
fmt.Printf("Status: %d\n", resp.StatusCode())
fmt.Printf("Body: %s\n", string(resp.Body()))
}
```
@ -47,49 +49,49 @@ You can check out [examples](examples.md) for more examples!
```go
type Client struct {
mu sync.RWMutex
mu sync.RWMutex
fasthttp *fasthttp.Client
fasthttp *fasthttp.Client
baseURL string
userAgent string
referer string
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
baseURL string
userAgent string
referer string
header *Header
params *QueryParam
cookies *Cookie
path *PathParam
debug bool
debug bool
timeout time.Duration
timeout time.Duration
// user defined request hooks
userRequestHooks []RequestHook
// user defined request hooks
userRequestHooks []RequestHook
// client package defined request hooks
builtinRequestHooks []RequestHook
// client package defined request hooks
builtinRequestHooks []RequestHook
// user defined response hooks
userResponseHooks []ResponseHook
// user defined response hooks
userResponseHooks []ResponseHook
// client package defined response hooks
builtinResponseHooks []ResponseHook
// client package defined response hooks
builtinResponseHooks []ResponseHook
jsonMarshal utils.JSONMarshal
jsonUnmarshal utils.JSONUnmarshal
xmlMarshal utils.XMLMarshal
xmlUnmarshal utils.XMLUnmarshal
jsonMarshal utils.JSONMarshal
jsonUnmarshal utils.JSONUnmarshal
xmlMarshal utils.XMLMarshal
xmlUnmarshal utils.XMLUnmarshal
cookieJar *CookieJar
cookieJar *CookieJar
// proxy
proxyURL string
// proxy
proxyURL string
// retry
retryConfig *RetryConfig
// retry
retryConfig *RetryConfig
// logger
logger log.CommonLogger
// logger
logger log.CommonLogger
}
```
@ -175,21 +177,21 @@ It can be used to configure request data while sending requests using Get, Post,
```go
type Config struct {
Ctx context.Context
Ctx context.Context
UserAgent string
Referer string
Header map[string]string
Param map[string]string
Cookie map[string]string
PathParam map[string]string
UserAgent string
Referer string
Header map[string]string
Param map[string]string
Cookie map[string]string
PathParam map[string]string
Timeout time.Duration
MaxRedirects int
Timeout time.Duration
MaxRedirects int
Body any
FormData map[string]string
File []*File
Body any
FormData map[string]string
File []*File
}
```
@ -393,7 +395,7 @@ cc.SetBaseURL("https://httpbin.org/")
resp, err := cc.Get("/get")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -402,12 +404,13 @@ fmt.Println(string(resp.Body()))
<details>
<summary>Click here to see the result</summary>
```
```json
{
"args": {},
...
}
```
</details>
### Header
@ -441,7 +444,7 @@ func (c *Client) SetHeader(key, val string) *Client
#### AddHeaders
AddHeaders method adds multiple headers field and its values at one go in the client instance.
These headers will be applied to all requests raised from this client instance.
These headers will be applied to all requests raised from this client instance.
Also it can be overridden at request level headers options.
```go title="Signature"
@ -451,7 +454,7 @@ func (c *Client) AddHeaders(h map[string][]string) *Client
#### SetHeaders
SetHeaders method sets multiple headers field and its values at one go in the client instance.
These headers will be applied to all requests raised from this client instance.
These headers will be applied to all requests raised from this client instance.
Also it can be overridden at request level headers options.
```go title="Signature"
@ -489,7 +492,7 @@ func (c *Client) SetParam(key, val string) *Client
#### AddParams
AddParams method adds multiple query params field and its values at one go in the client instance.
These params will be applied to all requests raised from this client instance.
These params will be applied to all requests raised from this client instance.
Also it can be overridden at request level params options.
```go title="Signature"
@ -499,7 +502,7 @@ func (c *Client) AddParams(m map[string][]string) *Client
#### SetParams
SetParams method sets multiple params field and its values at one go in the client instance.
These params will be applied to all requests raised from this client instance.
These params will be applied to all requests raised from this client instance.
Also it can be overridden at request level params options.
```go title="Signature"
@ -509,7 +512,7 @@ func (c *Client) SetParams(m map[string]string) *Client
#### SetParamsWithStruct
SetParamsWithStruct method sets multiple params field and its values at one go in the client instance.
These params will be applied to all requests raised from this client instance.
These params will be applied to all requests raised from this client instance.
Also it can be overridden at request level params options.
```go title="Signature"
@ -566,7 +569,7 @@ func (c *Client) SetPathParam(key, val string) *Client
#### SetPathParams
SetPathParams method sets multiple path params field and its values at one go in the client instance.
These path params will be applied to all requests raised from this client instance.
These path params will be applied to all requests raised from this client instance.
Also it can be overridden at request level path params options.
```go title="Signature"
@ -576,7 +579,7 @@ func (c *Client) SetPathParams(m map[string]string) *Client
#### SetPathParamsWithStruct
SetPathParamsWithStruct method sets multiple path params field and its values at one go in the client instance.
These path params will be applied to all requests raised from this client instance.
These path params will be applied to all requests raised from this client instance.
Also it can be overridden at request level path params options.
```go title="Signature"
@ -616,7 +619,7 @@ cc.SetCookie("john", "doe")
resp, err := cc.Get("https://httpbin.org/cookies")
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(resp.Body()))
@ -625,19 +628,20 @@ fmt.Println(string(resp.Body()))
<details>
<summary>Click here to see the result</summary>
```
```json
{
"cookies": {
"john": "doe"
}
}
```
</details>
#### SetCookies
SetCookies method sets multiple cookies field and its values at one go in the client instance.
These cookies will be applied to all requests raised from this client instance.
These cookies will be applied to all requests raised from this client instance.
Also it can be overridden at request level cookie options.
```go title="Signature"

View File

@ -20,7 +20,7 @@ Routes and other application-specific logic can live in as many files as you wis
## How do I handle custom 404 responses?
If you're using v2.32.0 or later, all you need to do is to implement a custom error handler. See below, or see a more detailed explanation at [Error Handling](../guide/error-handling.md#custom-error-handler).
If you're using v2.32.0 or later, all you need to do is to implement a custom error handler. See below, or see a more detailed explanation at [Error Handling](../guide/error-handling.md#custom-error-handler).
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:
@ -32,12 +32,13 @@ app.Use(func(c fiber.Ctx) error {
## How can i use live reload ?
[Air](https://github.com/cosmtrek/air) is a handy tool that automatically restarts your Go applications whenever the source code changes, making your development process faster and more efficient.
[Air](https://github.com/air-verse/air) is a handy tool that automatically restarts your Go applications whenever the source code changes, making your development process faster and more efficient.
To use Air in a Fiber project, follow these steps:
1. Install Air by downloading the appropriate binary for your operating system from the GitHub release page or by building the tool directly from source.
2. Create a configuration file for Air in your project directory. This file can be named, for example, .air.toml or air.conf. Here's a sample configuration file that works with Fiber:
* Install Air by downloading the appropriate binary for your operating system from the GitHub release page or by building the tool directly from source.
* Create a configuration file for Air in your project directory. This file can be named, for example, .air.toml or air.conf. Here's a sample configuration file that works with Fiber:
```toml
# .air.toml
root = "."
@ -50,7 +51,9 @@ tmp_dir = "tmp"
include_ext = ["go", "tpl", "tmpl", "html"]
exclude_regex = ["_test\\.go"]
```
3. Start your Fiber application using Air by running the following command in the terminal:
* Start your Fiber application using Air by running the following command in the terminal:
```sh
air
```
@ -59,7 +62,6 @@ As you make changes to your source code, Air will detect them and automatically
A complete example demonstrating the use of Air with Fiber can be found in the [Fiber Recipes repository](https://github.com/gofiber/recipes/tree/master/air). This example shows how to configure and use Air in a Fiber project to create an efficient development environment.
## How do I set up an error handler?
To override the default error handler, you can override the default when providing a [Config](../api/fiber.md#errorhandler) when initiating a new [Fiber instance](../api/fiber.md#new).
@ -92,78 +94,80 @@ To learn more about using Templates in Fiber, see [Templates](../guide/templates
## Does Fiber have a community chat?
Yes, we have our own [Discord ](https://gofiber.io/discord)server, where we hang out. We have different rooms for every subject.
Yes, we have our own [Discord](https://gofiber.io/discord)server, where we hang out. We have different rooms for every subject.
If you have questions or just want to have a chat, feel free to join us via this **&gt;** [**invite link**](https://gofiber.io/discord) **&lt;**.
![](/img/support-discord.png)
## Does fiber support sub domain routing ?
Yes we do, here are some examples:
Yes we do, here are some examples:
This example works v2
```go
package main
import (
"log"
"log"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
)
type Host struct {
Fiber *fiber.App
Fiber *fiber.App
}
func main() {
// Hosts
hosts := map[string]*Host{}
//-----
// API
//-----
api := fiber.New()
api.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
hosts["api.localhost:3000"] = &Host{api}
api.Get("/", func(c fiber.Ctx) error {
return c.SendString("API")
})
//------
// Blog
//------
blog := fiber.New()
blog.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
hosts["blog.localhost:3000"] = &Host{blog}
blog.Get("/", func(c fiber.Ctx) error {
return c.SendString("Blog")
})
//---------
// Website
//---------
site := fiber.New()
site.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
// Hosts
hosts := map[string]*Host{}
//-----
// API
//-----
api := fiber.New()
api.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
hosts["api.localhost:3000"] = &Host{api}
api.Get("/", func(c fiber.Ctx) error {
return c.SendString("API")
})
//------
// Blog
//------
blog := fiber.New()
blog.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
hosts["blog.localhost:3000"] = &Host{blog}
blog.Get("/", func(c fiber.Ctx) error {
return c.SendString("Blog")
})
//---------
// Website
//---------
site := fiber.New()
site.Use(logger.New(logger.Config{
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
}))
hosts["localhost:3000"] = &Host{site}
site.Get("/", func(c fiber.Ctx) error {
return c.SendString("Website")
})
// Server
app := fiber.New()
app.Use(func(c fiber.Ctx) error {
host := hosts[c.Hostname()]
if host == nil {
return c.SendStatus(fiber.StatusNotFound)
} else {
host.Fiber.Handler()(c.Context())
return nil
}
})
log.Fatal(app.Listen(":3000"))
hosts["localhost:3000"] = &Host{site}
site.Get("/", func(c fiber.Ctx) error {
return c.SendString("Website")
})
// Server
app := fiber.New()
app.Use(func(c fiber.Ctx) error {
host := hosts[c.Hostname()]
if host == nil {
return c.SendStatus(fiber.StatusNotFound)
} else {
host.Fiber.Handler()(c.Context())
return nil
}
})
log.Fatal(app.Listen(":3000"))
}
```
If more information is needed, please refer to this issue [#750](https://github.com/gofiber/fiber/issues/750)

View File

@ -24,6 +24,7 @@ app.Get("/", func(c fiber.Ctx) error {
return c.SendFile("file-does-not-exist")
})
```
</TabItem>
</Tabs>

View File

@ -5,13 +5,13 @@ sidebar_position: 7
---
## Custom JSON Encoder/Decoder
Since Fiber v2.32.0, we use **encoding/json** as default json library due to stability and producibility. However, the standard library is a bit slow compared to 3rd party libraries. If you're not happy with the performance of **encoding/json**, we recommend you to use these libraries:
Since Fiber v2.32.0, we have adopted `encoding/json` as the default JSON library for its stability and reliability. However, the standard library can be slower than some third-party alternatives. If you find the performance of `encoding/json` unsatisfactory, we suggest considering these libraries:
- [goccy/go-json](https://github.com/goccy/go-json)
- [bytedance/sonic](https://github.com/bytedance/sonic)
- [segmentio/encoding](https://github.com/segmentio/encoding)
- [mailru/easyjson](https://github.com/mailru/easyjson)
- [minio/simdjson-go](https://github.com/minio/simdjson-go)
- [wI2L/jettison](https://github.com/wI2L/jettison)
```go title="Example"
package main
@ -20,12 +20,12 @@ import "github.com/gofiber/fiber/v3"
import "github.com/goccy/go-json"
func main() {
app := fiber.New(fiber.Config{
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
})
app := fiber.New(fiber.Config{
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
})
# ...
# ...
}
```

View File

@ -10,23 +10,23 @@ In general, the Group functionality in Fiber behaves similarly to ExpressJS. Gro
## Paths
Like **Routing**, groups can also have paths that belong to a cluster.
Like `Routing`, groups can also have paths that belong to a cluster.
```go
func main() {
app := fiber.New()
app := fiber.New()
api := app.Group("/api", middleware) // /api
api := app.Group("/api", middleware) // /api
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
log.Fatal(app.Listen(":3000"))
}
```
@ -34,19 +34,19 @@ A **Group** of paths can have an optional handler.
```go
func main() {
app := fiber.New()
app := fiber.New()
api := app.Group("/api") // /api
api := app.Group("/api") // /api
v1 := api.Group("/v1") // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v1 := api.Group("/v1") // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2") // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
v2 := api.Group("/v2") // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
log.Fatal(app.Listen(":3000"))
}
```

View File

@ -20,12 +20,12 @@ import RoutingHandler from './../partials/routing/handler.md';
Route paths, combined with a request method, define the endpoints at which requests can be made. Route paths can be **strings** or **string patterns**.
**Examples of route paths based on strings**
### Examples of route paths based on strings
```go
// This route path will match requests to the root route, "/":
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("root")
return c.SendString("root")
})
// This route path will match requests to "/about":
@ -56,7 +56,7 @@ Greedy parameters are indicated by wildcard\(\*\) or plus\(+\) signs.
The routing also offers the possibility to use optional parameters, for the named parameters these are marked with a final "?", unlike the plus sign which is not optional, you can use the wildcard character for a parameter range which is optional and greedy.
**Example of define routes with route parameters**
### Example of define routes with route parameters
```go
// Parameters
@ -143,6 +143,7 @@ app.Get("/v1/*/shop/*", handler)
We have adapted the routing strongly to the express routing, but currently without the possibility of the regular expressions, because they are quite slow. The possibilities can be tested with version 0.1.7 \(express 4\) in the online [Express route tester](http://forbeslindesay.github.io/express-route-tester/).
### Constraints
Route constraints execute when a match has occurred to the incoming URL and the URL path is tokenized into route values by parameters. The feature was introduced in `v2.37.0` and inspired by [.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-6.0#route-constraints).
:::caution
@ -172,7 +173,7 @@ Constraints aren't validation for parameters. If constraints aren't valid for a
```go
app.Get("/:test<min(5)>", func(c fiber.Ctx) error {
return c.SendString(c.Params("test"))
return c.SendString(c.Params("test"))
})
// curl -X GET http://localhost:3000/12
@ -181,13 +182,15 @@ app.Get("/:test<min(5)>", func(c fiber.Ctx) error {
// curl -X GET http://localhost:3000/1
// Cannot GET /1
```
</TabItem>
<TabItem value="multiple-constraints" label="Multiple Constraints">
You can use `;` for multiple constraints.
```go
app.Get("/:test<min(100);maxLen(5)>", func(c fiber.Ctx) error {
return c.SendString(c.Params("test"))
return c.SendString(c.Params("test"))
})
// curl -X GET http://localhost:3000/120000
@ -199,13 +202,15 @@ app.Get("/:test<min(100);maxLen(5)>", func(c fiber.Ctx) error {
// curl -X GET http://localhost:3000/250
// 250
```
</TabItem>
<TabItem value="regex-constraint" label="Regex Constraint">
Fiber precompiles regex query when to register routes. So there're no performance overhead for regex constraint.
```go
app.Get(`/:date<regex(\d{4}-\d{2}-\d{2})>`, func(c fiber.Ctx) error {
return c.SendString(c.Params("date"))
return c.SendString(c.Params("date"))
})
// curl -X GET http://localhost:3000/125
@ -245,21 +250,21 @@ app.Get("/:test<int>?", func(c fiber.Ctx) error {
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.
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
// 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
// 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
}
```
@ -267,30 +272,30 @@ You can check the example below:
```go
type UlidConstraint struct {
fiber.CustomConstraint
fiber.CustomConstraint
}
func (*UlidConstraint) Name() string {
return "ulid"
return "ulid"
}
func (*UlidConstraint) Execute(param string, args ...string) bool {
_, err := ulid.Parse(param)
return err == nil
_, err := ulid.Parse(param)
return err == nil
}
func main() {
app := fiber.New()
app.RegisterCustomConstraint(&UlidConstraint{})
app := fiber.New()
app.RegisterCustomConstraint(&UlidConstraint{})
app.Get("/login/:id<ulid>", func(c fiber.Ctx) error {
return c.SendString("...")
})
app.Get("/login/:id<ulid>", func(c fiber.Ctx) error {
return c.SendString("...")
})
app.Listen(":3000")
app.Listen(":3000")
// /login/01HK7H9ZE5BFMK348CPYP14S0Z -> 200
// /login/12345 -> 404
// /login/01HK7H9ZE5BFMK348CPYP14S0Z -> 200
// /login/12345 -> 404
}
```
@ -302,15 +307,15 @@ Functions that are designed to make changes to the request or response are calle
```go
app.Use(func(c fiber.Ctx) error {
// Set a custom header on all responses:
c.Set("X-Custom-Header", "Hello, World")
// Set a custom header on all responses:
c.Set("X-Custom-Header", "Hello, World")
// Go to next middleware:
return c.Next()
// Go to next middleware:
return c.Next()
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
return c.SendString("Hello, World!")
})
```
@ -322,26 +327,25 @@ app.Get("/", func(c fiber.Ctx) error {
Adding routes dynamically after the application has started is not supported due to design and performance considerations. Make sure to define all your routes before the application starts.
:::
## Grouping
If you have many endpoints, you can organize your routes using `Group`.
```go
func main() {
app := fiber.New()
app := fiber.New()
api := app.Group("/api", middleware) // /api
api := app.Group("/api", middleware) // /api
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v1 := api.Group("/v1", middleware) // /api/v1
v1.Get("/list", handler) // /api/v1/list
v1.Get("/user", handler) // /api/v1/user
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
v2 := api.Group("/v2", middleware) // /api/v2
v2.Get("/list", handler) // /api/v2/list
v2.Get("/user", handler) // /api/v2/user
log.Fatal(app.Listen(":3000"))
log.Fatal(app.Listen(":3000"))
}
```

View File

@ -8,47 +8,29 @@ sidebar_position: 3
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
## Template interfaces
Templates are a great tool to render dynamic content without using a separate frontend framework.
Fiber provides a Views interface to provide your own template engine:
## Template Engines
<Tabs>
<TabItem value="views" label="Views">
Fiber allows you to provide a custom template engine at app initialization.
```go
type Views interface {
Load() error
Render(out io.Writer, name string, binding any, layout ...string) error
}
```
</TabItem>
</Tabs>
`Views` interface contains a `Load` and `Render` method, `Load` is executed by Fiber on app initialization to load/parse the templates.
```go
// Pass engine to Fiber's Views Engine
app := fiber.New(fiber.Config{
// Pass in Views Template Engine
Views: engine,
// Views Layout is the global layout for all template render until override on Render function.
ViewsLayout: "layouts/main"
// Default global path to search for views (can be overriden when calling Render())
ViewsLayout: "layouts/main",
// Enables/Disables access to `ctx.Locals()` entries in rendered views
// (defaults to false)
PassLocalsToViews: false,
})
```
The `Render` method is linked to the [**ctx.Render\(\)**](../api/ctx.md#render) function that accepts a template name and binding data. It will use global layout if layout is not being defined in `Render` function.
If the Fiber config option `PassLocalsToViews` is enabled, then all locals set using `ctx.Locals(key, value)` will be passed to the template.
### Supported Engines
```go
app.Get("/", func(c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"hello": "world",
});
})
```
## Engines
Fiber team maintains [templates](https://docs.gofiber.io/template) package that provides wrappers for multiple template engines:
The Fiber team maintains a [templates](https://docs.gofiber.io/template) package that provides wrappers for multiple template engines:
* [ace](https://docs.gofiber.io/template/ace/)
* [amber](https://docs.gofiber.io/template/amber/)
@ -60,6 +42,174 @@ Fiber team maintains [templates](https://docs.gofiber.io/template) package that
* [pug](https://docs.gofiber.io/template/pug)
* [slim](https://docs.gofiber.io/template/slim)
:::info
Custom template engines can implement the `Views` interface to be supported in Fiber.
:::
```go title="Views interface"
type Views interface {
// Fiber executes Load() on app initialization to load/parse the templates
Load() error
// Outputs a template to the provided buffer using the provided template,
// template name, and binded data
Render(io.Writer, string, interface{}, ...string) error
}
```
:::note
The `Render` method is linked to the [**ctx.Render\(\)**](../api/ctx.md#render) function that accepts a template name and binding data.
:::
## Rendering Templates
Once an engine is set up, a route handler can call the [**ctx.Render\(\)**](../api/ctx.md#render) function with a template name and binded data to send the rendered template.
```go title="Signature"
func (c Ctx) Render(name string, bind Map, layouts ...string) error
```
:::info
By default, [**ctx.Render\(\)**](../api/ctx.md#render) searches for the template name in the `ViewsLayout` path. To override this setting, provide the path(s) in the `layouts` argument.
:::
<Tabs>
<TabItem value="example" label="Example">
```go
app.Get("/", func(c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Title": "Hello, World!",
})
})
```
</TabItem>
<TabItem value="index" label="layouts/index.html">
```html
<!DOCTYPE html>
<html>
<body>
<h1>{{.Title}}</h1>
</body>
</html>
```
</TabItem>
</Tabs>
:::caution
If the Fiber config option `PassLocalsToViews` is enabled, then all locals set using `ctx.Locals(key, value)` will be passed to the template. It is important to avoid clashing keys when using this setting.
:::
## Advanced Templating
### Custom Functions
Fiber supports adding custom functions to templates.
#### AddFunc
Adds a global function to all templates.
```go title="Signature"
func (e *Engine) AddFunc(name string, fn interface{}) IEngineCore
```
<Tabs>
<TabItem value="add-func-example" label="AddFunc Example">
```go
// Add `ToUpper` to engine
engine := html.New("./views", ".html")
engine.AddFunc("ToUpper", func(s string) string {
return strings.ToUpper(s)
}
// Initialize Fiber App
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/", func (c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Content": "hello, World!"
})
})
```
</TabItem>
<TabItem value="add-func-template" label="views/index.html">
```html
<!DOCTYPE html>
<html>
<body>
<p>This will be in {{ToUpper "all caps"}}:</p>
<p>{{ToUpper .Content}}</p>
</body>
</html>
```
</TabItem>
</Tabs>
#### AddFuncMap
Adds a Map of functions (keyed by name) to all templates.
```go title="Signature"
func (e *Engine) AddFuncMap(m map[string]interface{}) IEngineCore
```
<Tabs>
<TabItem value="add-func-map-example" label="AddFuncMap Example">
```go
// Add `ToUpper` to engine
engine := html.New("./views", ".html")
engine.AddFuncMap(map[string]interface{}{
"ToUpper": func(s string) string {
return strings.ToUpper(s)
},
})
// Initialize Fiber App
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/", func (c fiber.Ctx) error {
return c.Render("index", fiber.Map{
"Content": "hello, world!"
})
})
```
</TabItem>
<TabItem value="add-func-map-template" label="views/index.html">
```html
<!DOCTYPE html>
<html>
<body>
<p>This will be in {{ToUpper "all caps"}}:</p>
<p>{{ToUpper .Content}}</p>
</body>
</html>
```
</TabItem>
</Tabs>
* For more advanced template documentation, please visit the [gofiber/template GitHub Repository](https://github.com/gofiber/template).
## Full Example
<Tabs>
<TabItem value="example" label="Example">
@ -75,9 +225,10 @@ import (
func main() {
// Initialize standard Go html template engine
engine := html.New("./views", ".html")
// If you want other engine, just replace with following
// If you want to use another engine,
// just replace with following:
// Create a new engine with django
// engine := django.New("./views", ".django")
// engine := django.New("./views", ".django")
app := fiber.New(fiber.Config{
Views: engine,
@ -85,22 +236,32 @@ func main() {
app.Get("/", func(c fiber.Ctx) error {
// Render index template
return c.Render("index", fiber.Map{
"Title": "Hello, World!",
})
"Title": "Go Fiber Template Example",
"Description": "An example template",
"Greeting": "Hello, World!",
});
})
log.Fatal(app.Listen(":3000"))
}
```
</TabItem>
<TabItem value="index" label="views/index.html">
```markup
```html
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<meta name="description" content="{{.Description}}">
</head>
<body>
<h1>{{.Title}}</h1>
<p>{{.Greeting}}</p>
</body>
</html>
```
</TabItem>
</Tabs>

View File

@ -19,21 +19,20 @@ func Convert[T any](value string, convertor func(string) (T, error), defaultValu
```go title="Example"
// GET http://example.com/id/bb70ab33-d455-4a03-8d78-d3c1dacae9ff
app.Get("/id/:id", func(c fiber.Ctx) error {
fiber.Convert(c.Params("id"), uuid.Parse) // UUID(bb70ab33-d455-4a03-8d78-d3c1dacae9ff), nil
fiber.Convert(c.Params("id"), uuid.Parse) // UUID(bb70ab33-d455-4a03-8d78-d3c1dacae9ff), nil
// GET http://example.com/search?id=65f6f54221fb90e6a6b76db7
app.Get("/search", func(c fiber.Ctx) error) {
fiber.Convert(c.Query("id"), mongo.ParseObjectID) // objectid(65f6f54221fb90e6a6b76db7), nil
fiber.Convert(c.Query("id"), uuid.Parse) // uuid.Nil, error(cannot parse given uuid)
fiber.Convert(c.Query("id"), uuid.Parse, mongo.NewObjectID) // new object id generated and return nil as error.
fiber.Convert(c.Query("id"), mongo.ParseObjectID) // objectid(65f6f54221fb90e6a6b76db7), nil
fiber.Convert(c.Query("id"), uuid.Parse) // uuid.Nil, error(cannot parse given uuid)
fiber.Convert(c.Query("id"), uuid.Parse, mongo.NewObjectID) // new object id generated and return nil as error.
}
// ...
})
```
### GetReqHeader
GetReqHeader function utilizing Go's generics feature.
@ -45,11 +44,11 @@ func GetReqHeader[V any](c Ctx, key string, defaultValue ...V) V
```go title="Example"
app.Get("/search", func(c fiber.Ctx) error {
// curl -X GET http://example.com/search -H "X-Request-ID: 12345" -H "X-Request-Name: John"
GetReqHeader[int](c, "X-Request-ID") // => returns 12345 as integer.
GetReqHeader[string](c, "X-Request-Name") // => returns "John" as string.
GetReqHeader[string](c, "unknownParam", "default") // => returns "default" as string.
// ...
// curl -X GET http://example.com/search -H "X-Request-ID: 12345" -H "X-Request-Name: John"
GetReqHeader[int](c, "X-Request-ID") // => returns 12345 as integer.
GetReqHeader[string](c, "X-Request-Name") // => returns "John" as string.
GetReqHeader[string](c, "unknownParam", "default") // => returns "default" as string.
// ...
})
```
@ -69,20 +68,20 @@ func Locals[V any](c Ctx, key any, value ...V) V
```go title="Example"
app.Use("/user/:user/:id", func(c fiber.Ctx) error {
// set local values
fiber.Locals[string](c, "user", "john")
fiber.Locals[int](c, "id", 25)
// ...
return c.Next()
// set local values
fiber.Locals[string](c, "user", "john")
fiber.Locals[int](c, "id", 25)
// ...
return c.Next()
})
app.Get("/user/*", func(c fiber.Ctx) error {
// get local values
name := fiber.Locals[string](c, "user") // john
age := fiber.Locals[int](c, "id") // 25
// ...
// get local values
name := fiber.Locals[string](c, "user") // john
age := fiber.Locals[int](c, "id") // 25
// ...
})
```
@ -97,11 +96,11 @@ func Params[V any](c Ctx, key string, defaultValue ...V) V
```go title="Example"
app.Get("/user/:user/:id", func(c fiber.Ctx) error {
// http://example.com/user/john/25
Params[int](c, "id") // => returns 25 as integer.
Params[int](c, "unknownParam", 99) // => returns the default 99 as integer.
// ...
return c.SendString("Hello, " + fiber.Params[string](c, "user"))
// http://example.com/user/john/25
Params[int](c, "id") // => returns 25 as integer.
Params[int](c, "unknownParam", 99) // => returns the default 99 as integer.
// ...
return c.SendString("Hello, " + fiber.Params[string](c, "user"))
})
```
@ -116,10 +115,10 @@ func Query[V any](c Ctx, key string, defaultValue ...V) V
```go title="Example"
app.Get("/search", func(c fiber.Ctx) error {
// http://example.com/search?name=john&age=25
Query[string](c, "name") // => returns "john"
Query[int](c, "age") // => returns 25 as integer.
Query[string](c, "unknownParam", "default") // => returns "default" as string.
// ...
// http://example.com/search?name=john&age=25
Query[string](c, "name") // => returns "john"
Query[int](c, "age") // => returns 25 as integer.
Query[string](c, "unknownParam", "default") // => returns "default" as string.
// ...
})
```

View File

@ -27,18 +27,18 @@ app := fiber.New(fiber.Config{
})
type User struct {
Name string `json:"name" form:"name" query:"name" validate:"required"`
Age int `json:"age" form:"age" query:"age" validate:"gte=0,lte=100"`
Name string `json:"name" form:"name" query:"name" validate:"required"`
Age int `json:"age" form:"age" query:"age" validate:"gte=0,lte=100"`
}
app.Post("/", func(c fiber.Ctx) error {
user := new(User)
// Works with all bind methods - Body, Query, Form, ...
if err := c.Bind().Body(user); err != nil { // <- here you receive the validation errors
return err
}
return c.JSON(user)
user := new(User)
// Works with all bind methods - Body, Query, Form, ...
if err := c.Bind().Body(user); err != nil { // <- here you receive the validation errors
return err
}
return c.JSON(user)
})
```

View File

@ -12,7 +12,7 @@ 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.21` or higher is required.
First of all, [download](https://go.dev/dl/) and install Go. `1.22` 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:
@ -54,10 +54,10 @@ We created a custom `CopyString` function that does the above and is available u
```go
app.Get("/:foo", func(c fiber.Ctx) error {
// Variable is now immutable
result := utils.CopyString(c.Params("foo"))
// Variable is now immutable
result := utils.CopyString(c.Params("foo"))
// ...
// ...
})
```
@ -65,13 +65,13 @@ Alternatively, you can also use the `Immutable` setting. It will make all values
```go
app := fiber.New(fiber.Config{
Immutable: true,
Immutable: true,
})
```
For more information, please check [**\#426**](https://github.com/gofiber/fiber/issues/426), [**\#185**](https://github.com/gofiber/fiber/issues/185) and [**\#3012**](https://github.com/gofiber/fiber/issues/3012).
### Hello, World!
### Hello, World
Embedded below is essentially the most straightforward **Fiber** app you can create:
@ -81,13 +81,13 @@ package main
import "github.com/gofiber/fiber/v3"
func main() {
app := fiber.New()
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
app.Listen(":3000")
}
```
@ -115,48 +115,48 @@ app.Method(path string, ...func(fiber.Ctx) error)
- `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
**Simple route**
#### Simple route
```go
// Respond with "Hello, World!" on root path, "/"
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
return c.SendString("Hello, World!")
})
```
**Parameters**
#### Parameters
```go
// GET http://localhost:8080/hello%20world
app.Get("/:value", func(c fiber.Ctx) error {
return c.SendString("value: " + c.Params("value"))
// => Get request with value: hello world
return c.SendString("value: " + c.Params("value"))
// => Get request with value: hello world
})
```
**Optional parameter**
#### Optional parameter
```go
// GET http://localhost:3000/john
app.Get("/:name?", func(c fiber.Ctx) error {
if c.Params("name") != "" {
return c.SendString("Hello " + c.Params("name"))
// => Hello john
}
return c.SendString("Where is john?")
if c.Params("name") != "" {
return c.SendString("Hello " + c.Params("name"))
// => Hello john
}
return c.SendString("Where is john?")
})
```
**Wildcards**
#### Wildcards
```go
// GET http://localhost:3000/api/user/john
app.Get("/api/*", func(c fiber.Ctx) error {
return c.SendString("API path: " + c.Params("*"))
// => API path: user/john
return c.SendString("API path: " + c.Params("*"))
// => API path: user/john
})
```
@ -183,8 +183,3 @@ http://localhost:3000/hello.html
http://localhost:3000/js/jquery.js
http://localhost:3000/css/style.css
```
### Note
For more information on how to build APIs in Go with Fiber, please check out this excellent article
[on building an express-style API in Go with Fiber](https://blog.logrocket.com/express-style-api-go-fiber/).

View File

@ -7,6 +7,7 @@ id: adaptor
Converter for net/http handlers to/from Fiber request handlers, special thanks to [@arsmn](https://github.com/arsmn)!
## Signatures
| Name | Signature | Description
| :--- | :--- | :---
| HTTPHandler | `HTTPHandler(h http.Handler) fiber.Handler` | http.Handler -> fiber.Handler
@ -21,149 +22,154 @@ Converter for net/http handlers to/from Fiber request handlers, special thanks t
## Examples
### net/http to Fiber
```go
package main
import (
"fmt"
"net/http"
"fmt"
"net/http"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
)
func main() {
// New fiber app
app := fiber.New()
// New fiber app
app := fiber.New()
// http.Handler -> fiber.Handler
app.Get("/", adaptor.HTTPHandler(handler(greet)))
// http.Handler -> fiber.Handler
app.Get("/", adaptor.HTTPHandler(handler(greet)))
// http.HandlerFunc -> fiber.Handler
app.Get("/func", adaptor.HTTPHandlerFunc(greet))
// http.HandlerFunc -> fiber.Handler
app.Get("/func", adaptor.HTTPHandlerFunc(greet))
// Listen on port 3000
app.Listen(":3000")
// Listen on port 3000
app.Listen(":3000")
}
func handler(f http.HandlerFunc) http.Handler {
return http.HandlerFunc(f)
return http.HandlerFunc(f)
}
func greet(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World!")
fmt.Fprint(w, "Hello World!")
}
```
### net/http middleware to Fiber
```go
package main
import (
"log"
"net/http"
"log"
"net/http"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
)
func main() {
// New fiber app
app := fiber.New()
// New fiber app
app := fiber.New()
// http middleware -> fiber.Handler
app.Use(adaptor.HTTPMiddleware(logMiddleware))
// http middleware -> fiber.Handler
app.Use(adaptor.HTTPMiddleware(logMiddleware))
// Listen on port 3000
app.Listen(":3000")
// Listen on port 3000
app.Listen(":3000")
}
func logMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("log middleware")
next.ServeHTTP(w, r)
})
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("log middleware")
next.ServeHTTP(w, r)
})
}
```
### Fiber Handler to net/http
```go
package main
import (
"net/http"
"net/http"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
)
func main() {
// fiber.Handler -> http.Handler
http.Handle("/", adaptor.FiberHandler(greet))
// fiber.Handler -> http.Handler
http.Handle("/", adaptor.FiberHandler(greet))
// fiber.Handler -> http.HandlerFunc
http.HandleFunc("/func", adaptor.FiberHandlerFunc(greet))
// fiber.Handler -> http.HandlerFunc
http.HandleFunc("/func", adaptor.FiberHandlerFunc(greet))
// Listen on port 3000
http.ListenAndServe(":3000", nil)
// Listen on port 3000
http.ListenAndServe(":3000", nil)
}
func greet(c fiber.Ctx) error {
return c.SendString("Hello World!")
return c.SendString("Hello World!")
}
```
### Fiber App to net/http
```go
package main
import (
"net/http"
"net/http"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
)
func main() {
app := fiber.New()
app := fiber.New()
app.Get("/greet", greet)
app.Get("/greet", greet)
// Listen on port 3000
http.ListenAndServe(":3000", adaptor.FiberApp(app))
// Listen on port 3000
http.ListenAndServe(":3000", adaptor.FiberApp(app))
}
func greet(c fiber.Ctx) error {
return c.SendString("Hello World!")
return c.SendString("Hello World!")
}
```
### Fiber Context to (net/http).Request
```go
package main
import (
"net/http"
"net/http"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/adaptor"
)
func main() {
app := fiber.New()
app := fiber.New()
app.Get("/greet", greetWithHTTPReq)
app.Get("/greet", greetWithHTTPReq)
// Listen on port 3000
http.ListenAndServe(":3000", adaptor.FiberApp(app))
// Listen on port 3000
http.ListenAndServe(":3000", adaptor.FiberApp(app))
}
func greetWithHTTPReq(c fiber.Ctx) error {
httpReq, err := adaptor.ConvertRequest(c, false)
if err != nil {
return err
}
httpReq, err := adaptor.ConvertRequest(c, false)
if err != nil {
return err
}
return c.SendString("Request URL: " + httpReq.URL.String())
return c.SendString("Request URL: " + httpReq.URL.String())
}
```

View File

@ -20,8 +20,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/basicauth"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/basicauth"
)
```

View File

@ -52,7 +52,7 @@ app.Use(cache.New(cache.Config{
return time.Second * time.Duration(newCacheTime)
},
KeyGenerator: func(c fiber.Ctx) string {
return utils.CopyString(c.Path())
return utils.CopyString(c.Path())
},
}))

View File

@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/compress"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/compress"
)
```
@ -40,10 +40,10 @@ app.Use(compress.New(compress.Config{
// Skip middleware for specific routes
app.Use(compress.New(compress.Config{
Next: func(c fiber.Ctx) bool {
return c.Path() == "/dont_compress"
},
Level: compress.LevelBestSpeed, // 1
Next: func(c fiber.Ctx) bool {
return c.Path() == "/dont_compress"
},
Level: compress.LevelBestSpeed, // 1
}))
```

View File

@ -26,8 +26,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/cors"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/cors"
)
```
@ -49,8 +49,8 @@ app.Use(cors.New())
// Or extend your config for customization
app.Use(cors.New(cors.Config{
AllowOrigins: "https://gofiber.io, https://gofiber.net",
AllowHeaders: "Origin, Content-Type, Accept",
AllowOrigins: []string{"https://gofiber.io", "https://gofiber.net"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept"},
}))
```
@ -69,25 +69,25 @@ If you need to allow wildcard origins, use `AllowOrigins` with a wildcard `"*"`
```go
// dbCheckOrigin checks if the origin is in the list of allowed origins in the database.
func dbCheckOrigin(db *sql.DB, origin string) bool {
// Placeholder query - adjust according to your database schema and query needs
query := "SELECT COUNT(*) FROM allowed_origins WHERE origin = $1"
var count int
err := db.QueryRow(query, origin).Scan(&count)
if err != nil {
// Handle error (e.g., log it); for simplicity, we return false here
return false
}
return count > 0
// Placeholder query - adjust according to your database schema and query needs
query := "SELECT COUNT(*) FROM allowed_origins WHERE origin = $1"
var count int
err := db.QueryRow(query, origin).Scan(&count)
if err != nil {
// Handle error (e.g., log it); for simplicity, we return false here
return false
}
return count > 0
}
// ...
app.Use(cors.New(cors.Config{
AllowOriginsFunc: func(origin string) bool {
return dbCheckOrigin(db, origin)
},
AllowOriginsFunc: func(origin string) bool {
return dbCheckOrigin(db, origin)
},
}))
```
@ -104,7 +104,7 @@ app.Use(cors.New(cors.Config{
This will result in the following panic:
```
```text
panic: [CORS] Configuration error: When 'AllowCredentials' is set to true, 'AllowOrigins' cannot contain a wildcard origin '*'. Please specify allowed origins explicitly or adjust 'AllowCredentials' setting.
```
@ -130,22 +130,22 @@ If AllowOrigins is a zero value `[]string{}`, and AllowOriginsFunc is provided,
```go
var ConfigDefault = Config{
Next: nil,
AllowOriginsFunc: nil,
AllowOrigins: []string{"*"},
AllowMethods: []string{
fiber.MethodGet,
fiber.MethodPost,
fiber.MethodHead,
fiber.MethodPut,
fiber.MethodDelete,
fiber.MethodPatch,
},
AllowHeaders: []string{},
AllowCredentials: false,
ExposeHeaders: []string{},
MaxAge: 0,
AllowPrivateNetwork: false,
Next: nil,
AllowOriginsFunc: nil,
AllowOrigins: []string{"*"},
AllowMethods: []string{
fiber.MethodGet,
fiber.MethodPost,
fiber.MethodHead,
fiber.MethodPut,
fiber.MethodDelete,
fiber.MethodPatch,
},
AllowHeaders: []string{},
AllowCredentials: false,
ExposeHeaders: []string{},
MaxAge: 0,
AllowPrivateNetwork: false,
}
```
@ -163,7 +163,7 @@ app.Use(cors.New(cors.Config{
}))
```
# How It Works
## How It Works
The CORS middleware works by adding the necessary CORS headers to responses from your Fiber application. These headers tell browsers what origins, methods, and headers are allowed for cross-origin requests.
@ -189,7 +189,7 @@ The `AllowMethods` option controls which HTTP methods are allowed. For example,
The `AllowHeaders` option specifies which headers are allowed in the actual request. The middleware sets the Access-Control-Allow-Headers response header to the value of `AllowHeaders`. This informs the client which headers it can use in the actual request.
The `AllowCredentials` option indicates whether the response to the request can be exposed when the credentials flag is true. If `AllowCredentials` is set to `true`, the middleware adds the header `Access-Control-Allow-Credentials: true` to the response. To prevent security vulnerabilities, `AllowCredentials` cannot be set to `true` if `AllowOrigins` is set to a wildcard (`*`).
The `AllowCredentials` option indicates whether the response to the request can be exposed when the credentials flag is true. If `AllowCredentials` is set to `true`, the middleware adds the header `Access-Control-Allow-Credentials: true` to the response. To prevent security vulnerabilities, `AllowCredentials` cannot be set to `true` if `AllowOrigins` is set to a wildcard (`*`).
The `ExposeHeaders` option defines a whitelist of headers that clients are allowed to access. If `ExposeHeaders` is set to `"X-Custom-Header"`, the middleware adds the header `Access-Control-Expose-Headers: X-Custom-Header` to the response.

View File

@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework:
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/csrf"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/csrf"
)
```
@ -31,12 +31,12 @@ app.Use(csrf.New())
// Or extend your config for customization
app.Use(csrf.New(csrf.Config{
KeyLookup: "header:X-Csrf-Token",
CookieName: "csrf_",
CookieSameSite: "Lax",
Expiration: 1 * time.Hour,
KeyGenerator: utils.UUIDv4,
Extractor: func(c fiber.Ctx) (string, error) { ... },
KeyLookup: "header:X-Csrf-Token",
CookieName: "csrf_",
CookieSameSite: "Lax",
Expiration: 1 * time.Hour,
KeyGenerator: utils.UUIDv4,
Extractor: func(c fiber.Ctx) (string, error) { ... },
}))
```
@ -48,27 +48,27 @@ 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(`<form action="/post" method="POST">
<input type="hidden" name="%s" value="%s">
<input type="text" name="message">
<input type="submit" value="Submit">
</form>`, formKey, token)
c.Set("Content-Type", "text/html")
return c.SendString(tmpl)
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(`<form action="/post" method="POST">
<input type="hidden" name="%s" value="%s">
<input type="text" name="message">
<input type="submit" value="Submit">
</form>`, formKey, token)
c.Set("Content-Type", "text/html")
return c.SendString(tmpl)
}
```
@ -78,11 +78,11 @@ There are two basic use cases for the CSRF middleware:
1. **Without Sessions**: This is the simplest way to use the middleware. It uses the Double Submit Cookie Pattern and does not require a user session.
- See GoFiber recipe [CSRF](https://github.com/gofiber/recipes/tree/master/csrf) for an example of using the CSRF middleware without a user session.
- See GoFiber recipe [CSRF](https://github.com/gofiber/recipes/tree/master/csrf) for an example of using the CSRF middleware without a user session.
2. **With Sessions**: This is generally considered more secure. It uses the Synchronizer Token Pattern and requires a user session, and the use of pre-session, which prevents login CSRF attacks.
- See GoFiber recipe [CSRF with Session](https://github.com/gofiber/recipes/tree/master/csrf-with-session) for an example of using the CSRF middleware with a user session.
- See GoFiber recipe [CSRF with Session](https://github.com/gofiber/recipes/tree/master/csrf-with-session) for an example of using the CSRF middleware with a user session.
## Signatures
@ -94,7 +94,6 @@ func HandlerFromContext(c fiber.Ctx) *Handler
func (h *Handler) DeleteToken(c fiber.Ctx) error
```
## Config
| Property | Type | Description | Default |
@ -122,14 +121,14 @@ func (h *Handler) DeleteToken(c fiber.Ctx) error
```go
var ConfigDefault = Config{
KeyLookup: "header:" + HeaderName,
CookieName: "csrf_",
CookieSameSite: "Lax",
Expiration: 1 * time.Hour,
KeyGenerator: utils.UUIDv4,
ErrorHandler: defaultErrorHandler,
Extractor: FromHeader(HeaderName),
SessionKey: "csrfToken",
KeyLookup: "header:" + HeaderName,
CookieName: "csrf_",
CookieSameSite: "Lax",
Expiration: 1 * time.Hour,
KeyGenerator: utils.UUIDv4,
ErrorHandler: defaultErrorHandler,
Extractor: FromHeader(HeaderName),
SessionKey: "csrfToken",
}
```
@ -139,18 +138,18 @@ It's recommended to use this middleware with [fiber/middleware/session](https://
```go
var ConfigDefault = Config{
KeyLookup: "header:" + HeaderName,
CookieName: "__Host-csrf_",
CookieSameSite: "Lax",
CookieSecure: true,
CookieSessionOnly: true,
CookieHTTPOnly: true,
Expiration: 1 * time.Hour,
KeyGenerator: utils.UUIDv4,
ErrorHandler: defaultErrorHandler,
Extractor: FromHeader(HeaderName),
Session: session.Store,
SessionKey: "csrfToken",
KeyLookup: "header:" + HeaderName,
CookieName: "__Host-csrf_",
CookieSameSite: "Lax",
CookieSecure: true,
CookieSessionOnly: true,
CookieHTTPOnly: true,
Expiration: 1 * time.Hour,
KeyGenerator: utils.UUIDv4,
ErrorHandler: defaultErrorHandler,
Extractor: FromHeader(HeaderName),
Session: session.Store,
SessionKey: "csrfToken",
}
```
@ -166,7 +165,7 @@ In the following example, the CSRF middleware will allow requests from `trusted.
```go
app.Use(csrf.New(csrf.Config{
TrustedOrigins: []string{"https://trusted.example.com"},
TrustedOrigins: []string{"https://trusted.example.com"},
}))
```
@ -176,7 +175,7 @@ In the following example, the CSRF middleware will allow requests from any subdo
```go
app.Use(csrf.New(csrf.Config{
TrustedOrigins: []string{"https://*.example.com"},
TrustedOrigins: []string{"https://*.example.com"},
}))
```
@ -216,19 +215,19 @@ 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 {
accepts := c.Accepts("html", "json")
path := c.Path()
if accepts == "json" || strings.HasPrefix(path, "/api/") {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"error": "Forbidden",
})
}
return c.Status(fiber.StatusForbidden).Render("error", fiber.Map{
"Title": "Forbidden",
"Status": fiber.StatusForbidden,
}, "layouts/main")
},
ErrorHandler: func(c fiber.Ctx, err error) error {
accepts := c.Accepts("html", "json")
path := c.Path()
if accepts == "json" || strings.HasPrefix(path, "/api/") {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"error": "Forbidden",
})
}
return c.Status(fiber.StatusForbidden).Render("error", fiber.Map{
"Title": "Forbidden",
"Status": fiber.StatusForbidden,
}, "layouts/main")
},
}))
```
@ -239,17 +238,17 @@ You can use any storage from our [storage](https://github.com/gofiber/storage/)
```go
storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3
app.Use(csrf.New(csrf.Config{
Storage: storage,
Storage: storage,
}))
```
# How It Works
## How It Works
## Token Generation
### 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 `csrf.TokenFromContext(c)`.
## Security Considerations
### Security Considerations
This middleware is designed to protect against CSRF attacks but does not protect against other attack vectors, such as XSS. It should be used in combination with other security measures.
@ -257,9 +256,9 @@ This middleware is designed to protect against CSRF attacks but does not protect
Never use 'safe' methods to mutate data, for example, never use a GET request to modify a resource. This middleware will not protect against CSRF attacks on 'safe' methods.
:::
### Token Validation Patterns
## Token Validation Patterns
#### Double Submit Cookie Pattern (Default)
### Double Submit Cookie Pattern (Default)
By default, the middleware generates and stores tokens using the `fiber.Storage` interface. These tokens are not linked to any particular user session, and they are validated using the Double Submit Cookie pattern. The token is stored in a cookie, and then sent as a header on requests. The middleware compares the cookie value with the header value to validate the token. This is a secure pattern that does not require a user session.
@ -273,7 +272,7 @@ When using this pattern, it's important to set the `CookieSameSite` option to `L
When using this pattern, this middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for Storage saves data to memory. See [Custom Storage/Database](#custom-storagedatabase) for customizing the storage.
:::
#### Synchronizer Token Pattern (with Session)
### Synchronizer Token Pattern (with Session)
When using this middleware with a user session, the middleware can be configured to store the token within the session. This method is recommended when using a user session, as it is generally more secure than the Double Submit Cookie Pattern.
@ -283,7 +282,7 @@ When using this pattern it's important to regenerate the session when the author
Pre-sessions are required and will be created automatically if not present. Use a session value to indicate authentication instead of relying on the presence of a session.
:::
### Defense In Depth
## Defense In Depth
When using this middleware, it's recommended to serve your pages over HTTPS, set the `CookieSecure` option to `true`, and set the `CookieSameSite` option to `Lax` or `Strict`. This ensures that the cookie is only sent over HTTPS and not on requests from external sites.
@ -293,7 +292,7 @@ Cookie prefixes `__Host-` and `__Secure-` can be used to further secure the cook
To use these prefixes, set the `CookieName` option to `__Host-csrf_` or `__Secure-csrf_`.
:::
### Referer Checking
## Referer Checking
For HTTPS requests, this middleware performs strict referer checking. Even if a subdomain can set or modify cookies on your domain, it can't force a user to post to your application, since that request won't come from your own exact domain.
@ -303,11 +302,11 @@ When HTTPS requests are protected by CSRF, referer checking is always carried ou
The Referer header is automatically included in requests by all modern browsers, including those made using the JS Fetch API. However, if you're making use of this middleware with a custom client, it's important to ensure that the client sends a valid Referer header.
:::
### Token Lifecycle
## Token Lifecycle
Tokens are valid until they expire or until they are deleted. By default, tokens are valid for 1 hour, and each subsequent request extends the expiration by 1 hour. The token only expires if the user doesn't make a request for the duration of the expiration time.
#### Token Reuse
### Token Reuse
By default, tokens may be used multiple times. If you want to delete the token after it has been used, you can set the `SingleUseToken` option to `true`. This will delete the token after it has been used, and a new token will be generated on the next request.
@ -315,16 +314,16 @@ By default, tokens may be used multiple times. If you want to delete the token a
Using `SingleUseToken` comes with usability trade-offs and is not enabled by default. For example, it can interfere with the user experience if the user has multiple tabs open or uses the back button.
:::
#### Deleting Tokens
### Deleting Tokens
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
handler := csrf.HandlerFromContext(ctx)
if handler != nil {
if err := handler.DeleteToken(app.AcquireCtx(ctx)); err != nil {
// handle error
}
if err := handler.DeleteToken(app.AcquireCtx(ctx)); err != nil {
// handle error
}
}
```
@ -332,6 +331,6 @@ if handler != nil {
If you are using this middleware with the fiber session middleware, then you can simply call `session.Destroy()`, `session.Regenerate()`, or `session.Reset()` to delete the session and the token stored therein.
:::
### BREACH
## BREACH
It's important to note that the token is sent as a header on every request. If you include the token in a page that is vulnerable to [BREACH](https://en.wikipedia.org/wiki/BREACH), an attacker may be able to extract the token. To mitigate this, ensure your pages are served over HTTPS, disable HTTP compression, and implement rate limiting for requests.

View File

@ -11,8 +11,8 @@ Make sure to enable fiber's `EnableTrustedProxyCheck` config option before using
Also be aware that enabling support for early data in your reverse proxy (e.g. nginx, as done with a simple `ssl_early_data on;`) makes requests replayable. Refer to the following documents before continuing:
- https://datatracker.ietf.org/doc/html/rfc8446#section-8
- https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt/
- [datatracker](https://datatracker.ietf.org/doc/html/rfc8446#section-8)
- [trailofbits](https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt)
By default, this middleware allows early data requests on safe HTTP request methods only and rejects the request otherwise, i.e. aborts the request before executing your handler. This behavior can be controlled by the `AllowEarlyData` config option.
Safe HTTP methods — `GET`, `HEAD`, `OPTIONS` and `TRACE` — should not modify a state on the server.
@ -30,8 +30,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/earlydata"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/earlydata"
)
```
@ -43,8 +43,8 @@ app.Use(earlydata.New())
// Or extend your config for customization
app.Use(earlydata.New(earlydata.Config{
Error: fiber.ErrTooEarly,
// ...
Error: fiber.ErrTooEarly,
// ...
}))
```
@ -61,13 +61,13 @@ app.Use(earlydata.New(earlydata.Config{
```go
var ConfigDefault = Config{
IsEarlyData: func(c fiber.Ctx) bool {
return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue
},
AllowEarlyData: func(c fiber.Ctx) bool {
return fiber.IsMethodSafe(c.Method())
},
Error: fiber.ErrTooEarly,
IsEarlyData: func(c fiber.Ctx) bool {
return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue
},
AllowEarlyData: func(c fiber.Ctx) bool {
return fiber.IsMethodSafe(c.Method())
},
Error: fiber.ErrTooEarly,
}
```
@ -75,7 +75,7 @@ var ConfigDefault = Config{
```go
const (
DefaultHeaderName = "Early-Data"
DefaultHeaderTrueValue = "1"
DefaultHeaderName = "Early-Data"
DefaultHeaderTrueValue = "1"
)
```

View File

@ -4,7 +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.
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.
@ -28,8 +28,8 @@ To use the Encrypt Cookie middleware, first, import the middleware package as pa
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/encryptcookie"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/encryptcookie"
)
```
@ -75,11 +75,11 @@ To generate a 32 char key, use `openssl rand -base64 32` or `encryptcookie.Gener
```go
var ConfigDefault = Config{
Next: nil,
Except: []string{},
Key: "",
Encryptor: EncryptCookie,
Decryptor: DecryptCookie,
Next: nil,
Except: []string{},
Key: "",
Encryptor: EncryptCookie,
Decryptor: DecryptCookie,
}
```
@ -91,20 +91,21 @@ You may also choose to exclude certain cookies from encryption. For instance, if
```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.ConfigDefault.CookieName}, // exclude CSRF cookie
}))
app.Use(csrf.New(csrf.Config{
KeyLookup: "header:" + csrf.HeaderName,
CookieSameSite: "Lax",
CookieSecure: true,
CookieHTTPOnly: false,
KeyLookup: "header:" + csrf.HeaderName,
CookieSameSite: "Lax",
CookieSecure: true,
CookieHTTPOnly: false,
}))
```
## Encryption Algorithms
The default Encryptor and Decryptor functions use `AES-256-GCM` for encryption and decryption. If you need to use `AES-128` or `AES-192` instead, you can do so by changing the length of the key when calling `encryptcookie.GenerateKey(length)` or by providing a key of one of the following lengths:
- AES-128 requires a 16-byte key.
- AES-192 requires a 24-byte key.
- AES-256 requires a 32-byte key.
@ -119,4 +120,4 @@ And for AES-192:
```go
key := encryptcookie.GenerateKey(24)
```
```

View File

@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/envvar"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/envvar"
)
```
@ -31,10 +31,10 @@ app.Use("/expose/envvars", envvar.New())
// Or extend your config for customization
app.Use("/expose/envvars", envvar.New(
envvar.Config{
ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"},
ExcludeVars: map[string]string{"excludeKey": ""},
}),
envvar.Config{
ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"},
ExcludeVars: map[string]string{"excludeKey": ""},
}),
)
```
@ -45,7 +45,8 @@ You will need to provide a path to use the envvar middleware.
## Response
Http response contract:
```
```json
{
"vars": {
"someEnvVariable": "someValue",

View File

@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/etag"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/etag"
)
```

View File

@ -18,12 +18,13 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
expvarmw "github.com/gofiber/fiber/v3/middleware/expvar"
"github.com/gofiber/fiber/v3"
expvarmw "github.com/gofiber/fiber/v3/middleware/expvar"
)
```
After you initiate your Fiber app, you can use the following possibilities:
```go
var count = expvar.NewInt("count")

View File

@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/favicon"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/favicon"
)
```
@ -55,9 +55,9 @@ app.Use(favicon.New(favicon.Config{
```go
var ConfigDefault = Config{
Next: nil,
File: "",
URL: fPath,
CacheControl: "public, max-age=31536000",
Next: nil,
File: "",
URL: fPath,
CacheControl: "public, max-age=31536000",
}
```

View File

@ -4,7 +4,7 @@ id: healthcheck
# Health Check
Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the liveness and readiness state of HTTP applications.
Liveness, readiness and startup probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides three endpoints for checking the liveness, readiness, and startup state of HTTP applications.
## Overview
@ -16,6 +16,10 @@ Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/
- **Default Endpoint**: `/readyz`
- **Behavior**: By default returns `true` immediately when the server is operational.
- **Startup Probe**: Checks if the application has completed its startup sequence and is ready to proceed with initialization and readiness checks.
- **Default Endpoint**: `/startupz`
- **Behavior**: By default returns `true` immediately when the server is operational.
- **HTTP Status Codes**:
- `200 OK`: Returned when the checker function evaluates to `true`.
- `503 Service Unavailable`: Returned when the checker function evaluates to `false`.
@ -29,6 +33,7 @@ func NewHealthChecker(config Config) fiber.Handler
## Examples
Import the middleware package that is part of the [Fiber](https://github.com/gofiber/fiber) web framework
```go
import(
"github.com/gofiber/fiber/v3"
@ -43,6 +48,8 @@ After you initiate your [Fiber](https://github.com/gofiber/fiber) app, you can u
app.Get(healthcheck.DefaultLivenessEndpoint, healthcheck.NewHealthChecker())
// Provide a minimal config for readiness check
app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker())
// Provide a minimal config for startup check
app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker())
// Provide a minimal config for check with custom endpoint
app.Get("/live", healthcheck.NewHealthChecker())
@ -58,6 +65,12 @@ app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker(healt
return true
},
}))
// And it works the same for startup, just change the route
app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{
Probe: func(c fiber.Ctx) bool {
return true
},
}))
// With a custom route and custom probe
app.Get("/live", healthcheck.NewHealthChecker(healthcheck.Config{
Probe: func(c fiber.Ctx) bool {
@ -79,29 +92,34 @@ app.All(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker(healt
```go
type Config struct {
// Next defines a function to skip this middleware when returned true. If this function returns true
// Next defines a function to skip this middleware when returned true. If this function returns true
// and no other handlers are defined for the route, Fiber will return a status 404 Not Found, since
// no other handlers were defined to return a different status.
//
// Optional. Default: nil
Next func(fiber.Ctx) bool
//
// Optional. Default: nil
Next func(fiber.Ctx) bool
// Function used for checking the liveness of the application. Returns true if the application
// is running and false if it is not. The liveness probe is typically used to indicate if
// the application is in a state where it can handle requests (e.g., the server is up and running).
//
// Optional. Default: func(c fiber.Ctx) bool { return true }
Probe HealthChecker
// Function used for checking the liveness of the application. Returns true if the application
// is running and false if it is not. The liveness probe is typically used to indicate if
// the application is in a state where it can handle requests (e.g., the server is up and running).
// The readiness probe is typically used to indicate if the application is ready to start accepting traffic (e.g., all necessary components
// are initialized and dependent services are available) and the startup probe typically used to
// indicate if the application has completed its startup sequence and is ready to proceed with
// initialization and readiness checks
//
// Optional. Default: func(c fiber.Ctx) bool { return true }
Probe HealthChecker
}
```
## Default Config
The default configuration used by this middleware is defined as follows:
```go
func defaultProbe(fiber.Ctx) bool { return true }
var ConfigDefault = Config{
Probe: defaultProbe,
Probe: defaultProbe,
}
```

View File

@ -13,30 +13,31 @@ func New(config ...Config) fiber.Handler
```
## Examples
```go
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/helmet"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/helmet"
)
func main() {
app := fiber.New()
app := fiber.New()
app.Use(helmet.New())
app.Use(helmet.New())
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome!")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome!")
})
app.Listen(":3000")
app.Listen(":3000")
}
```
**Test:**
## Test
```curl
```bash
curl -I http://localhost:3000
```
@ -67,16 +68,16 @@ curl -I http://localhost:3000
```go
var ConfigDefault = Config{
XSSProtection: "0",
ContentTypeNosniff: "nosniff",
XFrameOptions: "SAMEORIGIN",
ReferrerPolicy: "no-referrer",
CrossOriginEmbedderPolicy: "require-corp",
CrossOriginOpenerPolicy: "same-origin",
CrossOriginResourcePolicy: "same-origin",
OriginAgentCluster: "?1",
XDNSPrefetchControl: "off",
XDownloadOptions: "noopen",
XPermittedCrossDomain: "none",
XSSProtection: "0",
ContentTypeNosniff: "nosniff",
XFrameOptions: "SAMEORIGIN",
ReferrerPolicy: "no-referrer",
CrossOriginEmbedderPolicy: "require-corp",
CrossOriginOpenerPolicy: "same-origin",
CrossOriginResourcePolicy: "same-origin",
OriginAgentCluster: "?1",
XDNSPrefetchControl: "off",
XDownloadOptions: "noopen",
XPermittedCrossDomain: "none",
}
```

View File

@ -6,7 +6,7 @@ id: idempotency
Idempotency middleware for [Fiber](https://github.com/gofiber/fiber) allows for fault-tolerant APIs where duplicate requests — for example due to networking issues on the client-side — do not erroneously cause the same action performed multiple times on the server-side.
Refer to https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02 for a better understanding.
Refer to [datatracker](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02) for a better understanding.
## Signatures
@ -22,8 +22,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/idempotency"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/idempotency"
)
```
@ -39,8 +39,8 @@ app.Use(idempotency.New())
```go
app.Use(idempotency.New(idempotency.Config{
Lifetime: 42 * time.Minute,
// ...
Lifetime: 42 * time.Minute,
// ...
}))
```
@ -60,26 +60,26 @@ app.Use(idempotency.New(idempotency.Config{
```go
var ConfigDefault = Config{
Next: func(c fiber.Ctx) bool {
// Skip middleware if the request was done using a safe HTTP method
return fiber.IsMethodSafe(c.Method())
},
Next: func(c fiber.Ctx) bool {
// Skip middleware if the request was done using a safe HTTP method
return fiber.IsMethodSafe(c.Method())
},
Lifetime: 30 * time.Minute,
Lifetime: 30 * time.Minute,
KeyHeader: "X-Idempotency-Key",
KeyHeaderValidate: func(k string) error {
if l, wl := len(k), 36; l != wl { // UUID length is 36 chars
return fmt.Errorf("%w: invalid length: %d != %d", ErrInvalidIdempotencyKey, l, wl)
}
KeyHeader: "X-Idempotency-Key",
KeyHeaderValidate: func(k string) error {
if l, wl := len(k), 36; l != wl { // UUID length is 36 chars
return fmt.Errorf("%w: invalid length: %d != %d", ErrInvalidIdempotencyKey, l, wl)
}
return nil
},
return nil
},
KeepResponseHeaders: nil,
KeepResponseHeaders: nil,
Lock: nil, // Set in configDefault so we don't allocate data here.
Lock: nil, // Set in configDefault so we don't allocate data here.
Storage: nil, // Set in configDefault so we don't allocate data here.
Storage: nil, // Set in configDefault so we don't allocate data here.
}
```

View File

@ -19,44 +19,44 @@ func TokenFromContext(c fiber.Ctx) string
package main
import (
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
)
var (
apiKey = "correct horse battery staple"
apiKey = "correct horse battery staple"
)
func validateAPIKey(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
}
func main() {
app := fiber.New()
app := fiber.New()
// note that the keyauth middleware needs to be defined before the routes are defined!
app.Use(keyauth.New(keyauth.Config{
KeyLookup: "cookie:access_token",
Validator: validateAPIKey,
}))
// note that the keyauth middleware needs to be defined before the routes are defined!
app.Use(keyauth.New(keyauth.Config{
KeyLookup: "cookie:access_token",
Validator: validateAPIKey,
}))
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Listen(":3000")
app.Listen(":3000")
}
```
**Test:**
## Test
```bash
# No api-key specified -> 400 missing
@ -72,7 +72,6 @@ curl --cookie "access_token=Clearly A Wrong Key" http://localhost:3000
For a more detailed example, see also the [`github.com/gofiber/recipes`](https://github.com/gofiber/recipes) repository and specifically the `fiber-envoy-extauthz` repository and the [`keyauth example`](https://github.com/gofiber/recipes/blob/master/fiber-envoy-extauthz/authz/main.go) code.
### Authenticate only certain endpoints
If you want to authenticate only certain endpoints, you can use the `Config` of keyauth and apply a filter function (eg. `authFilter`) like so
@ -81,63 +80,63 @@ If you want to authenticate only certain endpoints, you can use the `Config` of
package main
import (
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
"regexp"
"strings"
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
"regexp"
"strings"
)
var (
apiKey = "correct horse battery staple"
protectedURLs = []*regexp.Regexp{
regexp.MustCompile("^/authenticated$"),
regexp.MustCompile("^/auth2$"),
}
apiKey = "correct horse battery staple"
protectedURLs = []*regexp.Regexp{
regexp.MustCompile("^/authenticated$"),
regexp.MustCompile("^/auth2$"),
}
)
func validateAPIKey(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
}
func authFilter(c fiber.Ctx) bool {
originalURL := strings.ToLower(c.OriginalURL())
originalURL := strings.ToLower(c.OriginalURL())
for _, pattern := range protectedURLs {
if pattern.MatchString(originalURL) {
return false
}
}
return true
for _, pattern := range protectedURLs {
if pattern.MatchString(originalURL) {
return false
}
}
return true
}
func main() {
app := fiber.New()
app := fiber.New()
app.Use(keyauth.New(keyauth.Config{
Next: authFilter,
KeyLookup: "cookie:access_token",
Validator: validateAPIKey,
}))
app.Use(keyauth.New(keyauth.Config{
Next: authFilter,
KeyLookup: "cookie:access_token",
Validator: validateAPIKey,
}))
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome")
})
app.Get("/authenticated", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Get("/auth2", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated 2!")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome")
})
app.Get("/authenticated", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Get("/auth2", func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated 2!")
})
app.Listen(":3000")
app.Listen(":3000")
}
```
@ -163,10 +162,10 @@ curl --cookie "access_token=correct horse battery staple" http://localhost:3000/
package main
import (
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
"crypto/sha256"
"crypto/subtle"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/keyauth"
)
const (
@ -174,29 +173,29 @@ const (
)
func main() {
app := fiber.New()
app := fiber.New()
authMiddleware := keyauth.New(keyauth.Config{
Validator: func(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))
authMiddleware := keyauth.New(keyauth.Config{
Validator: func(c fiber.Ctx, key string) (bool, error) {
hashedAPIKey := sha256.Sum256([]byte(apiKey))
hashedKey := sha256.Sum256([]byte(key))
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
},
})
if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 {
return true, nil
}
return false, keyauth.ErrMissingOrMalformedAPIKey
},
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome")
})
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Welcome")
})
app.Get("/allowed", authMiddleware, func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Get("/allowed", authMiddleware, func(c fiber.Ctx) error {
return c.SendString("Successfully authenticated!")
})
app.Listen(":3000")
app.Listen(":3000")
}
```
@ -228,23 +227,24 @@ curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000
```go
var ConfigDefault = Config{
SuccessHandler: func(c fiber.Ctx) error {
return c.Next()
},
ErrorHandler: func(c fiber.Ctx, err error) error {
if err == ErrMissingOrMalformedAPIKey {
return c.Status(fiber.StatusUnauthorized).SendString(err.Error())
}
return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key")
},
KeyLookup: "header:" + fiber.HeaderAuthorization,
CustomKeyLookup: nil,
AuthScheme: "Bearer",
SuccessHandler: func(c fiber.Ctx) error {
return c.Next()
},
ErrorHandler: func(c fiber.Ctx, err error) error {
if err == ErrMissingOrMalformedAPIKey {
return c.Status(fiber.StatusUnauthorized).SendString(err.Error())
}
return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key")
},
KeyLookup: "header:" + fiber.HeaderAuthorization,
CustomKeyLookup: nil,
AuthScheme: "Bearer",
}
```
## CustomKeyLookup
Two public utility functions are provided that may be useful when creating custom extraction:
* `DefaultKeyLookup(keyLookup string, authScheme string)`: This is the function that implements the default `KeyLookup` behavior, exposed to be used as a component of custom parsing logic
* `MultipleKeySourceLookup(keyLookups []string, authScheme string)`: Creates a CustomKeyLookup function that checks each listed source using the above function until a key is found or the options are all exhausted. For example, `MultipleKeySourceLookup([]string{"header:Authorization", "header:x-api-key", "cookie:apikey"}, "Bearer")` would first check the standard Authorization header, checks the `x-api-key` header next, and finally checks for a cookie named `apikey`. If any of these contain a valid API key, the request continues. Otherwise, an error is returned.

View File

@ -26,8 +26,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/limiter"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/limiter"
)
```
@ -43,6 +43,9 @@ app.Use(limiter.New(limiter.Config{
return c.IP() == "127.0.0.1"
},
Max: 20,
MaxFunc: func(c fiber.Ctx) int {
return 20
},
Expiration: 30 * time.Second,
KeyGenerator: func(c fiber.Ctx) string {
return c.Get("x-forwarded-for")
@ -68,10 +71,26 @@ app.Use(limiter.New(limiter.Config{
}))
```
This means that every window will take into account the previous window(if there was any). The given formula for the rate is:
This means that every window will consider the previous window (if there was any). The given formula for the rate is:
```text
weightOfPreviousWindow = previous window's amount request * (whenNewWindow / Expiration)
rate = weightOfPreviousWindow + current window's amount request.
```
weightOfPreviousWindpw = previous window's amount request * (whenNewWindow / Expiration)
rate = weightOfPreviousWindpw + current window's amount request.
## Dynamic limit
You can also calculate the limit dynamically using the MaxFunc parameter. It's a function that receives the request's context as a parameter and allow you to calculate a different limit for each request separately.
Example:
```go
app.Use(limiter.New(limiter.Config{
MaxFunc: func(c fiber.Ctx) int {
return getUserLimit(ctx.Param("id"))
},
Expiration: 30 * time.Second,
}))
```
## Config
@ -80,6 +99,7 @@ rate = weightOfPreviousWindpw + current window's amount request.
|:-----------------------|:--------------------------|:--------------------------------------------------------------------------------------------|:-----------------------------------------|
| 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 |
| MaxFunc | `func(fiber.Ctx) int` | A function to calculate the max number of recent connections during `Expiration` seconds before sending a 429 response. | A function which returns the cfg.Max |
| 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 |
@ -100,6 +120,9 @@ A custom store can be used if it implements the `Storage` interface - more detai
```go
var ConfigDefault = Config{
Max: 5,
MaxFunc: func(c fiber.Ctx) int {
return 5
},
Expiration: 1 * time.Minute,
KeyGenerator: func(c fiber.Ctx) string {
return c.IP()
@ -119,7 +142,8 @@ You can use any storage from our [storage](https://github.com/gofiber/storage/)
```go
storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3
app.Use(limiter.New(limiter.Config{
Storage: storage,
Storage: storage,
}))
```

View File

@ -7,9 +7,11 @@ id: logger
Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP request/response details.
## Signatures
```go
func New(config ...Config) fiber.Handler
```
## Examples
Import the middleware package that is part of the Fiber web framework
@ -88,7 +90,7 @@ app.Use(logger.New(logger.Config{
}))
```
:::tip
:::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.
:::
@ -112,6 +114,7 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that
| 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,
@ -126,6 +129,7 @@ var ConfigDefault = Config{
```
## Constants
```go
// Logger variables
const (

View File

@ -14,22 +14,25 @@ Monitor is still in beta, API might change in the future!
![](https://i.imgur.com/nHAtBpJ.gif)
### Signatures
## Signatures
```go
func New() fiber.Handler
```
### Examples
## Examples
Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/monitor"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/monitor"
)
```
After you initiate your Fiber app, you can use the following possibilities:
```go
// Initialize default config (Assign the middleware to /metrics)
app.Get("/metrics", monitor.New())
@ -39,43 +42,53 @@ app.Get("/metrics", monitor.New())
// and change the Title to `MyService Metrics Page`
app.Get("/metrics", monitor.New(monitor.Config{Title: "MyService Metrics Page"}))
```
You can also access the API endpoint with
`curl -X GET -H "Accept: application/json" http://localhost:3000/metrics` which returns:
```json
{"pid":{ "cpu":0.4568381746582226, "ram":20516864, "conns":3 },
"os": { "cpu":8.759124087593099, "ram":3997155328, "conns":44,
"total_ram":8245489664, "load_avg":0.51 }}
{
"pid":{
"cpu":0.4568381746582226,
"ram":20516864,
"conns":3
},
"os": {
"cpu":8.759124087593099, "ram":3997155328, "conns":44,
"total_ram":8245489664, "load_avg":0.51
}
}
```
## Config
| Property | Type | Description | Default |
|:-----------|:------------------------|:--------------------------------------------------------------------|:----------------------------------------------------------------------------|
| 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` |
| 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" |
| Property | Type | Description | Default |
|:-----------|:------------------------|:--------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------|
| 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` |
| CustomHead | `string` | Custom HTML Code to Head Section(Before End) | empty |
| FontURL | `string` | FontURL for specify font resource path or URL | "[fonts.googleapis.com](https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap)" |
| ChartJsURL | `string` | ChartJsURL for specify ChartJS library path or URL | "[cdn.jsdelivr.net](https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js)" |
## Default Config
```go
var ConfigDefault = Config{
Title: defaultTitle,
Refresh: defaultRefresh,
FontURL: defaultFontURL,
ChartJsURL: defaultChartJSURL,
CustomHead: defaultCustomHead,
APIOnly: false,
Next: nil,
index: newIndex(viewBag{
defaultTitle,
defaultRefresh,
defaultFontURL,
defaultChartJSURL,
defaultCustomHead,
}),
Title: defaultTitle,
Refresh: defaultRefresh,
FontURL: defaultFontURL,
ChartJsURL: defaultChartJSURL,
CustomHead: defaultCustomHead,
APIOnly: false,
Next: nil,
index: newIndex(viewBag{
defaultTitle,
defaultRefresh,
defaultFontURL,
defaultChartJSURL,
defaultCustomHead,
}),
}
```

View File

@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/pprof"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/pprof"
)
```

View File

@ -7,6 +7,7 @@ id: proxy
Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you to proxy requests to multiple servers.
## Signatures
// BalancerForward performs the given http request based on a round-robin balancer and fills the given http response.
```go
@ -154,18 +155,18 @@ app.Use(proxy.Balancer(proxy.Config{
## Config
| Property | Type | Description | Default |
|:----------------|:-----------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------|
| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` |
| Servers | `[]string` | Servers defines a list of `<scheme>://<host>` 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` |
| Timeout | `time.Duration` | Timeout is the request timeout used when calling the proxy client. | 1 second |
| ReadBufferSize | `int` | Per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers (for example, BIG cookies). | (Not specified) |
| WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | (Not specified) |
| TlsConfig | `*tls.Config` (or `*fasthttp.TLSConfig` in v3) | TLS config for the HTTP client. | `nil` |
| DialDualStack | `bool` | Client will attempt to connect to both IPv4 and IPv6 host addresses if set to true. | `false` |
| Client | `*fasthttp.LBClient` | Client is a custom client when client config is complex. | `nil` |
| Property | Type | Description | Default |
|:----------------|:-----------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------|
| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` |
| Servers | `[]string` | Servers defines a list of `<scheme>://<host>` HTTP servers, which are used in a round-robin manner. i.e.: "[https://foobar.com](https://foobar.com), [http://www.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` |
| Timeout | `time.Duration` | Timeout is the request timeout used when calling the proxy client. | 1 second |
| ReadBufferSize | `int` | Per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers (for example, BIG cookies). | (Not specified) |
| WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | (Not specified) |
| TlsConfig | `*tls.Config` (or `*fasthttp.TLSConfig` in v3) | TLS config for the HTTP client. | `nil` |
| DialDualStack | `bool` | Client will attempt to connect to both IPv4 and IPv6 host addresses if set to true. | `false` |
| Client | `*fasthttp.LBClient` | Client is a custom client when client config is complex. | `nil` |
## Default Config

View File

@ -18,8 +18,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/recover"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/recover"
)
```

View File

@ -18,35 +18,35 @@ func New(config ...Config) fiber.Handler
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/redirect"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/redirect"
)
func main() {
app := fiber.New()
app.Use(redirect.New(redirect.Config{
Rules: map[string]string{
"/old": "/new",
"/old/*": "/new/$1",
},
StatusCode: 301,
}))
app.Get("/new", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/new/*", func(c fiber.Ctx) error {
return c.SendString("Wildcard: " + c.Params("*"))
})
app.Listen(":3000")
app := fiber.New()
app.Use(redirect.New(redirect.Config{
Rules: map[string]string{
"/old": "/new",
"/old/*": "/new/$1",
},
StatusCode: 301,
}))
app.Get("/new", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/new/*", func(c fiber.Ctx) error {
return c.SendString("Wildcard: " + c.Params("*"))
})
app.Listen(":3000")
}
```
**Test:**
## Test
```curl
```bash
curl http://localhost:3000/old
curl http://localhost:3000/old/hello
```
@ -63,6 +63,6 @@ curl http://localhost:3000/old/hello
```go
var ConfigDefault = Config{
StatusCode: fiber.StatusFound,
StatusCode: fiber.StatusFound,
}
```

View File

@ -19,8 +19,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/requestid"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/requestid"
)
```
@ -58,6 +58,7 @@ func handler(c fiber.Ctx) error {
| Generator | `func() string` | Generator defines a function to generate the unique identifier. | utils.UUID |
## Default Config
The default config uses a fast UUID generator which will expose the number of
requests made to the server. To conceal this value for better privacy, use the
`utils.UUIDv4` generator.
@ -66,6 +67,6 @@ requests made to the server. To conceal this value for better privacy, use the
var ConfigDefault = Config{
Next: nil,
Header: fiber.HeaderXRequestID,
Generator: utils.UUID,
Generator: utils.UUID,
}
```

View File

@ -20,39 +20,40 @@ func New(config ...Config) fiber.Handler
| Rules | `map[string]string` | Rules defines the URL path rewrite rules. The values captured in asterisk can be retrieved by index. | (Required) |
### Examples
```go
package main
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/rewrite"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/rewrite"
)
func main() {
app := fiber.New()
app.Use(rewrite.New(rewrite.Config{
Rules: map[string]string{
"/old": "/new",
"/old/*": "/new/$1",
},
}))
app.Get("/new", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/new/*", func(c fiber.Ctx) error {
return c.SendString("Wildcard: " + c.Params("*"))
})
app.Listen(":3000")
app := fiber.New()
app.Use(rewrite.New(rewrite.Config{
Rules: map[string]string{
"/old": "/new",
"/old/*": "/new/$1",
},
}))
app.Get("/new", func(c fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/new/*", func(c fiber.Ctx) error {
return c.SendString("Wildcard: " + c.Params("*"))
})
app.Listen(":3000")
}
```
**Test:**
## Test
```curl
```bash
curl http://localhost:3000/old
curl http://localhost:3000/old/hello
```

View File

@ -37,11 +37,13 @@ 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/v3"
"github.com/gofiber/fiber/v3/middleware/session"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/session"
)
```
@ -76,15 +78,15 @@ app.Get("/", func(c fiber.Ctx) error {
panic(err)
}
// Sets a specific expiration for this session
sess.SetExpiry(time.Second * 2)
// Sets a specific expiration for this session
sess.SetExpiry(time.Second * 2)
// Save session
if err := sess.Save(); err != nil {
panic(err)
}
panic(err)
}
return c.SendString(fmt.Sprintf("Welcome %v", name))
return c.SendString(fmt.Sprintf("Welcome %v", name))
})
```
@ -108,11 +110,11 @@ app.Get("/", func(c fiber.Ctx) error {
```go
var ConfigDefault = Config{
Expiration: 24 * time.Hour,
KeyLookup: "cookie:session_id",
KeyGenerator: utils.UUIDv4,
source: "cookie",
sessionName: "session_id",
Expiration: 24 * time.Hour,
KeyLookup: "cookie:session_id",
KeyGenerator: utils.UUIDv4,
source: "cookie",
sessionName: "session_id",
}
```
@ -120,9 +122,9 @@ var ConfigDefault = Config{
```go
const (
SourceCookie Source = "cookie"
SourceHeader Source = "header"
SourceURLQuery Source = "query"
SourceCookie Source = "cookie"
SourceHeader Source = "header"
SourceURLQuery Source = "query"
)
```
@ -132,8 +134,9 @@ You can use any storage from our [storage](https://github.com/gofiber/storage/)
```go
storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3
store := session.New(session.Config{
Storage: storage,
Storage: storage,
})
```

View File

@ -7,16 +7,19 @@ id: skip
Skip middleware for [Fiber](https://github.com/gofiber/fiber) that skips a wrapped handler if a predicate is true.
## Signatures
```go
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/v3"
"github.com/gofiber/fiber/v3/middleware/skip"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/skip"
)
```
@ -24,21 +27,21 @@ After you initiate your Fiber app, you can use the following possibilities:
```go
func main() {
app := fiber.New()
app := fiber.New()
app.Use(skip.New(BasicHandler, func(ctx fiber.Ctx) bool {
return ctx.Method() == fiber.MethodGet
}))
app.Use(skip.New(BasicHandler, func(ctx fiber.Ctx) bool {
return ctx.Method() == fiber.MethodGet
}))
app.Get("/", func(ctx fiber.Ctx) error {
return ctx.SendString("It was a GET request!")
})
app.Get("/", func(ctx fiber.Ctx) error {
return ctx.SendString("It was a GET request!")
})
log.Fatal(app.Listen(":3000"))
log.Fatal(app.Listen(":3000"))
}
func BasicHandler(ctx fiber.Ctx) error {
return ctx.SendString("It was not a GET request!")
return ctx.SendString("It was not a GET request!")
}
```

View File

@ -19,6 +19,7 @@ func New(root string, cfg ...Config) fiber.Handler
## Examples
Import the middleware package that is part of the [Fiber](https://github.com/gofiber/fiber) web framework
```go
import(
"github.com/gofiber/fiber/v3"
@ -166,7 +167,7 @@ You can set `CacheDuration` config property to `-1` to disable caching.
```go
var ConfigDefault = Config{
Index: []string{"index.html"},
CacheDuration: 10 * time.Second,
Index: []string{"index.html"},
CacheDuration: 10 * time.Second,
}
```

View File

@ -6,13 +6,12 @@ id: timeout
There exist two distinct implementations of timeout middleware [Fiber](https://github.com/gofiber/fiber).
**New**
## New
As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`.
As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`.
If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`.
It does not cancel long running executions. Underlying executions must handle timeout by using `context.Context` parameter.
## Signatures
@ -27,8 +26,8 @@ Import the middleware package that is part of the Fiber web framework
```go
import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/timeout"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/timeout"
)
```
@ -36,31 +35,31 @@ After you initiate your Fiber app, you can use the following possibilities:
```go
func main() {
app := fiber.New()
h := func(c fiber.Ctx) error {
sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms")
if err := sleepWithContext(c.UserContext(), sleepTime); err != nil {
return fmt.Errorf("%w: execution error", err)
}
return nil
}
app := fiber.New()
h := func(c fiber.Ctx) error {
sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms")
if err := sleepWithContext(c.UserContext(), sleepTime); err != nil {
return fmt.Errorf("%w: execution error", err)
}
return nil
}
app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second))
log.Fatal(app.Listen(":3000"))
app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second))
log.Fatal(app.Listen(":3000"))
}
func sleepWithContext(ctx context.Context, d time.Duration) error {
timer := time.NewTimer(d)
timer := time.NewTimer(d)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return context.DeadlineExceeded
case <-timer.C:
}
return nil
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return context.DeadlineExceeded
case <-timer.C:
}
return nil
}
```
@ -82,30 +81,30 @@ Use with custom error:
var ErrFooTimeOut = errors.New("foo context canceled")
func main() {
app := fiber.New()
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)
}
return nil
}
app := fiber.New()
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)
}
return nil
}
app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut))
log.Fatal(app.Listen(":3000"))
app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut))
log.Fatal(app.Listen(":3000"))
}
func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error {
timer := time.NewTimer(d)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return ErrFooTimeOut
case <-timer.C:
}
return nil
timer := time.NewTimer(d)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
return ErrFooTimeOut
case <-timer.C:
}
return nil
}
```
@ -113,24 +112,24 @@ Sample usage with a DB call:
```go
func main() {
app := fiber.New()
db, _ := gorm.Open(postgres.Open("postgres://localhost/foodb"), &gorm.Config{})
app := fiber.New()
db, _ := gorm.Open(postgres.Open("postgres://localhost/foodb"), &gorm.Config{})
handler := func(ctx fiber.Ctx) error {
tran := db.WithContext(ctx.UserContext()).Begin()
if tran = tran.Exec("SELECT pg_sleep(50)"); tran.Error != nil {
return tran.Error
}
if tran = tran.Commit(); tran.Error != nil {
return tran.Error
}
handler := func(ctx fiber.Ctx) error {
tran := db.WithContext(ctx.UserContext()).Begin()
if tran = tran.Exec("SELECT pg_sleep(50)"); tran.Error != nil {
return tran.Error
}
if tran = tran.Commit(); tran.Error != nil {
return tran.Error
}
return nil
}
return nil
}
app.Get("/foo", timeout.New(handler, 10*time.Second))
log.Fatal(app.Listen(":3000"))
app.Get("/foo", timeout.New(handler, 10*time.Second))
log.Fatal(app.Listen(":3000"))
}
```

View File

@ -30,12 +30,12 @@ func (app *App) All(path string, handler Handler, middlewares ...Handler) Router
```go title="Examples"
// Simple GET handler
app.Get("/api/list", func(c fiber.Ctx) error {
return c.SendString("I'm a GET request!")
return c.SendString("I'm a GET request!")
})
// Simple POST handler
app.Post("/api/register", func(c fiber.Ctx) error {
return c.SendString("I'm a POST request!")
return c.SendString("I'm a POST request!")
})
```
@ -71,7 +71,7 @@ app.Use([]string{"/api", "/home"}, func(c fiber.Ctx) error {
// Attach multiple handlers
app.Use("/api", func(c fiber.Ctx) error {
c.Set("X-Custom-Header", random.String(32))
c.Set("X-Custom-Header", random.String(32))
return c.Next()
}, func(c fiber.Ctx) error {
return c.Next()

View File

@ -7,7 +7,7 @@ toc_max_heading_level: 3
:::caution
Its a draft, not finished yet.
It's a draft, not finished yet.
:::
@ -20,6 +20,7 @@ We are excited to announce the release of Fiber v3! 🚀
In this guide, we'll walk you through the most important changes in Fiber `v3` and show you how to migrate your existing Fiber `v2` applications to Fiber `v3`.
Here's a quick overview of the changes in Fiber `v3`:
- [🚀 App](#-app)
- [🗺️ Router](#-router)
- [🧠 Context](#-context)
@ -32,11 +33,12 @@ Here's a quick overview of the changes in Fiber `v3`:
- [Session](#session)
- [Filesystem](#filesystem)
- [Monitor](#monitor)
- [Healthcheck](#healthcheck)
- [📋 Migration guide](#-migration-guide)
## Drop for old Go versions
Fiber `v3` drops support for Go versions below `1.21`. We recommend upgrading to Go `1.21` or higher to use Fiber `v3`.
Fiber `v3` drops support for Go versions below `1.22`. We recommend upgrading to Go `1.22` or higher to use Fiber `v3`.
## 🚀 App
@ -46,33 +48,33 @@ DRAFT section
We have made several changes to the Fiber app, including:
* Listen -> unified with config
* Static -> has been removed and moved to [static middleware](./middleware/static.md)
* app.Config properties moved to listen config
* DisableStartupMessage
* EnablePrefork -> previously Prefork
* EnablePrintRoutes
* ListenerNetwork -> previously Network
- Listen -> unified with config
- Static -> has been removed and moved to [static middleware](./middleware/static.md)
- app.Config properties moved to listen config
- DisableStartupMessage
- EnablePrefork -> previously Prefork
- EnablePrintRoutes
- ListenerNetwork -> previously Network
### new methods
* RegisterCustomBinder
* RegisterCustomConstraint
* NewCtxFunc
- RegisterCustomBinder
- RegisterCustomConstraint
- NewCtxFunc
### removed methods
* Mount -> Use app.Use() instead
* ListenTLS -> Use app.Listen() with tls.Config
* ListenTLSWithCertificate -> Use app.Listen() with tls.Config
* ListenMutualTLS -> Use app.Listen() with tls.Config
* ListenMutualTLSWithCertificate -> Use app.Listen() with tls.Config
- Mount -> Use app.Use() instead
- ListenTLS -> Use app.Listen() with tls.Config
- ListenTLSWithCertificate -> Use app.Listen() with tls.Config
- ListenMutualTLS -> Use app.Listen() with tls.Config
- ListenMutualTLSWithCertificate -> Use app.Listen() with tls.Config
### Methods changes
* Test -> timeout changed to 1 second
* Listen -> has a config parameter
* Listener -> has a config parameter
- Test -> timeout changed to 1 second
- Listen -> has a config parameter
- Listener -> has a config parameter
### CTX interface + customizable
@ -141,6 +143,7 @@ app.Route("/api").Route("/user/:id?")
})
})
```
</details>
[Here](./api/app#route) you can find more information.
@ -169,6 +172,7 @@ api.Get("/user", func(c *fiber.Ctx) error {
// register subapp
app.Use("/api", api)
```
</details>
To enable the routing changes above we had to slightly adjust the signature of the `Add` method.
@ -192,37 +196,37 @@ DRAFT section
### new methods
* AutoFormat -> ExpressJs like
* Host -> ExpressJs like
* Port -> ExpressJs like
* IsProxyTrusted
* Reset
* Schema -> ExpressJs like
* SendStream -> ExpressJs like
* SendString -> ExpressJs like
* String -> ExpressJs like
* ViewBind -> instead of Bind
- AutoFormat -> ExpressJs like
- Host -> ExpressJs like
- Port -> ExpressJs like
- IsProxyTrusted
- Reset
- Schema -> ExpressJs like
- SendStream -> ExpressJs like
- SendString -> ExpressJs like
- String -> ExpressJs like
- ViewBind -> instead of Bind
### removed methods
* AllParams -> c.Bind().URL() ?
* ParamsInt -> Params Generic
* QueryBool -> Query Generic
* QueryFloat -> Query Generic
* QueryInt -> Query Generic
* BodyParser -> c.Bind().Body()
* CookieParser -> c.Bind().Cookie()
* ParamsParser -> c.Bind().URL()
* RedirectToRoute -> c.Redirect().Route()
* RedirectBack -> c.Redirect().Back()
* ReqHeaderParser -> c.Bind().Header()
- AllParams -> c.Bind().URL() ?
- ParamsInt -> Params Generic
- QueryBool -> Query Generic
- QueryFloat -> Query Generic
- QueryInt -> Query Generic
- BodyParser -> c.Bind().Body()
- CookieParser -> c.Bind().Cookie()
- ParamsParser -> c.Bind().URL()
- RedirectToRoute -> c.Redirect().Route()
- RedirectBack -> c.Redirect().Back()
- ReqHeaderParser -> c.Bind().Header()
### changed methods
* Bind -> for Binding instead of View, us c.ViewBind()
* Format -> Param: body interface{} -> handlers ...ResFmt
* Redirect -> c.Redirect().To()
* SendFile now supports different configurations using the config parameter.
- Bind -> for Binding instead of View, us c.ViewBind()
- Format -> Param: body interface{} -> handlers ...ResFmt
- Redirect -> c.Redirect().To()
- SendFile now supports different configurations using the config parameter.
---
@ -230,7 +234,7 @@ DRAFT section
The Gofiber client has been completely rebuilt. It includes numerous new features such as Cookiejar, request/response hooks, and more.
You can take a look to [client docs](./client/rest.md) to see what's new with the client.
## 📎 Binding
:::caution
@ -243,7 +247,6 @@ DRAFT section
DRAFT section
:::
## 🧰 Generic functions
:::caution
@ -252,6 +255,34 @@ DRAFT section
## 🧬 Middlewares
### Adaptor
The adaptor middleware has been significantly optimized for performance and efficiency. Key improvements include reduced response times, lower memory usage, and fewer memory allocations. These changes make the middleware more reliable and capable of handling higher loads effectively. Enhancements include the introduction of a `sync.Pool` for managing `fasthttp.RequestCtx` instances and better HTTP request and response handling between net/http and fasthttp contexts.
| Payload Size | Metric | V2 | V3 | Percent Change |
|--------------|------------------|-----------|----------|-------------------|
| 100KB | Execution Time | 1056 ns/op| 588.6 ns/op | -44.25% |
| | Memory Usage | 2644 B/op | 254 B/op | -90.39% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
| 500KB | Execution Time | 1061 ns/op| 562.9 ns/op | -46.94% |
| | Memory Usage | 2644 B/op | 248 B/op | -90.62% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
| 1MB | Execution Time | 1080 ns/op| 629.7 ns/op | -41.68% |
| | Memory Usage | 2646 B/op | 267 B/op | -89.91% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
| 5MB | Execution Time | 1093 ns/op| 540.3 ns/op | -50.58% |
| | Memory Usage | 2654 B/op | 254 B/op | -90.43% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
| 10MB | Execution Time | 1044 ns/op| 533.1 ns/op | -48.94% |
| | Memory Usage | 2665 B/op | 258 B/op | -90.32% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
| 25MB | Execution Time | 1069 ns/op| 540.7 ns/op | -49.42% |
| | Memory Usage | 2706 B/op | 289 B/op | -89.32% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
| 50MB | Execution Time | 1137 ns/op| 554.6 ns/op | -51.21% |
| | Memory Usage | 2734 B/op | 298 B/op | -89.10% |
| | Allocations | 16 allocs/op | 5 allocs/op | -68.75% |
### Cache
We are excited to introduce a new option in our caching middleware: Cache Invalidator. This feature provides greater control over cache management, allowing you to define a custom conditions for invalidating cache entries.
@ -261,9 +292,11 @@ We are excited to introduce a new option in our caching middleware: Cache Invali
We've made some changes to the CORS middleware to improve its functionality and flexibility. Here's what's new:
#### New Struct Fields
- `Config.AllowPrivateNetwork`: This new field is a boolean that allows you to control whether private networks are allowed. This is related to the [Private Network Access (PNA)](https://wicg.github.io/private-network-access/) specification from the Web Incubator Community Group (WICG). When set to `true`, the CORS middleware will allow CORS preflight requests from private networks and respond with the `Access-Control-Allow-Private-Network: true` header. This could be useful in development environments or specific use cases, but should be done with caution due to potential security risks.
#### Updated Struct Fields
We've updated several fields from a single string (containing comma-separated values) to slices, allowing for more explicit declaration of multiple values. Here are the updated fields:
- `Config.AllowOrigins`: Now accepts a slice of strings, each representing an allowed origin.
@ -287,7 +320,7 @@ DRAFT section
### Filesystem
We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware.
We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware.
Now, static middleware can do everything that filesystem middleware and static do. You can check out [static middleware](./middleware/static.md) or [migration guide](#-migration-guide) to see what has been changed.
### Monitor
@ -298,6 +331,25 @@ DRAFT section
Monitor middleware is now in Contrib package.
### Healthcheck
The Healthcheck middleware has been enhanced to support more than two routes, with default endpoints for liveliness, readiness, and startup checks. Here's a detailed breakdown of the changes and how to use the new features.
1. **Support for More Than Two Routes**:
- The updated middleware now supports multiple routes beyond the default liveliness and readiness endpoints. This allows for more granular health checks, such as startup probes.
2. **Default Endpoints**:
- Three default endpoints are now available:
- **Liveness**: `/livez`
- **Readiness**: `/readyz`
- **Startup**: `/startupz`
- These endpoints can be customized or replaced with user-defined routes.
3. **Simplified Configuration**:
- The configuration for each health check endpoint has been simplified. Each endpoint can be configured separately, allowing for more flexibility and readability.
Refer to the [healthcheck middleware migration guide](./middleware/healthcheck.md) or the [general migration guide](#-migration-guide) to review the changes.
## 📋 Migration guide
- [🚀 App](#-app-1)
@ -308,7 +360,6 @@ Monitor middleware is now in Contrib package.
- [🌎 Client package](#-client-package-1)
- [🧬 Middlewares](#-middlewares-1)
### 🚀 App
#### Static
@ -386,6 +437,31 @@ app.Route("/api").Route("/user/:id?")
});
```
### 🗺 RebuildTree
We have added a new method that allows the route tree stack to be rebuilt in runtime, with it, you can add a route while your application is running and rebuild the route tree stack to make it registered and available for calls.
You can find more reference on it in the [app](./api/app.md#rebuildtree):
#### Example Usage
```go
app.Get("/define", func(c Ctx) error { // Define a new route dynamically
app.Get("/dynamically-defined", func(c Ctx) error { // Adding a dynamically defined route
return c.SendStatus(http.StatusOK)
})
app.RebuildTree() // Rebuild the route tree to register the new route
return c.SendStatus(http.StatusOK)
})
```
In this example, a new route is defined and then `RebuildTree()` is called to make sure the new route is registered and available.
**Note:** Use this method with caution. It is **not** thread-safe and calling it can be very performance-intensive, so it should be used sparingly and only in
development mode. Avoid using it concurrently.
### 🧠 Context
### 📎 Parser
@ -449,3 +525,48 @@ app.Use(static.New("", static.Config{
MaxAge: 3600,
}))
```
### Healthcheck
Previously, the Healthcheck middleware was configured with a combined setup for liveliness and readiness probes:
```go
//before
app.Use(healthcheck.New(healthcheck.Config{
LivenessProbe: func(c *fiber.Ctx) bool {
return true
},
LivenessEndpoint: "/live",
ReadinessProbe: func(c *fiber.Ctx) bool {
return serviceA.Ready() && serviceB.Ready() && ...
},
ReadinessEndpoint: "/ready",
}))
```
With the new version, each health check endpoint is configured separately, allowing for more flexibility:
```go
// after
// Default liveness endpoint configuration
app.Get(healthcheck.DefaultLivenessEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{
Probe: func(c *fiber.Ctx) bool {
return true
},
}))
// Default readiness endpoint configuration
app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker())
// New default startup endpoint configuration
// Default endpoint is /startupz
app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{
Probe: func(c *fiber.Ctx) bool {
return serviceA.Ready() && serviceB.Ready() && ...
},
}))
// Custom liveness endpoint configuration
app.Get("/live", healthcheck.NewHealthChecker())
```

6
go.mod
View File

@ -1,9 +1,9 @@
module github.com/gofiber/fiber/v3
go 1.21
go 1.22
require (
github.com/gofiber/utils/v2 v2.0.0-beta.4
github.com/gofiber/utils/v2 v2.0.0-beta.6
github.com/google/uuid v1.6.0
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.20
@ -20,6 +20,8 @@ require (
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

8
go.sum
View File

@ -2,8 +2,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co=
github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY=
github.com/gofiber/utils/v2 v2.0.0-beta.6 h1:ED62bOmpRXdgviPlfTmf0Q+AXzhaTUAFtdWjgx+XkYI=
github.com/gofiber/utils/v2 v2.0.0-beta.6/go.mod h1:3Kz8Px3jInKFvqxDzDeoSygwEOO+3uyubTmUa6PqY+0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
@ -36,6 +36,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -56,6 +58,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@ -11,12 +11,12 @@ import (
// Group struct
type Group struct {
app *App
parentGroup *Group
name string
anyRouteDefined bool
app *App
parentGroup *Group
name string
Prefix string
Prefix string
anyRouteDefined bool
}
// Name Assign name to specific route or group itself.

View File

@ -31,11 +31,11 @@ import (
// along with quality, specificity, parameters, and order.
// Used for sorting accept headers.
type acceptedType struct {
params headerParams
spec string
quality float64
specificity int
order int
params headerParams
}
type headerParams map[string][]byte
@ -222,7 +222,7 @@ func getGroupPath(prefix, path string) string {
path = "/" + path
}
return strings.TrimRight(prefix, "/") + path
return utils.TrimRight(prefix, '/') + path
}
// acceptsOffer This function determines if an offer matches a given specification.
@ -298,7 +298,7 @@ func paramsMatch(specParamStr headerParams, offerParams string) bool {
for specParam, specVal := range specParamStr {
foundParam := false
fasthttp.VisitHeaderParams(utils.UnsafeBytes(offerParams), func(key, value []byte) bool {
if utils.EqualFold(specParam, string(key)) {
if utils.EqualFold(specParam, utils.UnsafeString(key)) {
foundParam = true
allSpecParamsMatch = utils.EqualFold(specVal, value)
return false
@ -326,7 +326,7 @@ func getSplicedStrList(headerValue string, dst []string) []string {
var (
index int
character rune
lastElementEndsAt uint8
lastElementEndsAt int
insertIndex int
)
for index, character = range headerValue + "$" {
@ -336,8 +336,8 @@ func getSplicedStrList(headerValue string, dst []string) []string {
dst = make([]string, len(dst)+(len(dst)>>1)+2)
copy(dst, oldSlice)
}
dst[insertIndex] = strings.TrimLeft(headerValue[lastElementEndsAt:index], " ")
lastElementEndsAt = uint8(index + 1)
dst[insertIndex] = utils.TrimLeft(headerValue[lastElementEndsAt:index], ' ')
lastElementEndsAt = index + 1
insertIndex++
}
}
@ -356,7 +356,7 @@ func forEachMediaRange(header []byte, functor func([]byte)) {
for len(header) > 0 {
n := 0
header = bytes.TrimLeft(header, " ")
header = utils.TrimLeft(header, ' ')
quotes := 0
escaping := false
@ -423,29 +423,31 @@ func getOffer(header []byte, isAccepted func(spec, offer string, specParams head
forEachMediaRange(header, func(accept []byte) {
order++
spec, quality := accept, 1.0
var params headerParams
if i := bytes.IndexByte(accept, ';'); i != -1 {
spec = accept[:i]
// The vast majority of requests will have only the q parameter with
// no whitespace. Check this first to see if we can skip
// the more involved parsing.
if bytes.HasPrefix(accept[i:], []byte(";q=")) && bytes.IndexByte(accept[i+3:], ';') == -1 {
if q, err := fasthttp.ParseUfloat(bytes.TrimRight(accept[i+3:], " ")); err == nil {
// Optimized quality parsing
qIndex := i + 3
if bytes.HasPrefix(accept[i:], []byte(";q=")) && bytes.IndexByte(accept[qIndex:], ';') == -1 {
if q, err := fasthttp.ParseUfloat(accept[qIndex:]); err == nil {
quality = q
}
} else {
params, _ = headerParamPool.Get().(headerParams) //nolint:errcheck // only contains headerParams
for k := range params {
delete(params, k)
}
fasthttp.VisitHeaderParams(accept[i:], func(key, value []byte) bool {
if string(key) == "q" {
if len(key) == 1 && key[0] == 'q' {
if q, err := fasthttp.ParseUfloat(value); err == nil {
quality = q
}
return false
}
params[utils.UnsafeString(utils.ToLowerBytes(key))] = value
lowerKey := utils.UnsafeString(utils.ToLowerBytes(key))
params[lowerKey] = value
return true
})
}
@ -457,13 +459,16 @@ func getOffer(header []byte, isAccepted func(spec, offer string, specParams head
}
}
spec = bytes.TrimRight(spec, " ")
spec = utils.Trim(spec, ' ')
// Get specificity
// Determine specificity
var specificity int
// check for wildcard this could be a mime */* or a wildcard character *
switch {
case string(spec) == "*/*" || string(spec) == "*":
case len(spec) == 1 && spec[0] == '*':
specificity = 1
case bytes.Equal(spec, []byte("*/*")):
specificity = 1
case bytes.HasSuffix(spec, []byte("/*")):
specificity = 2
@ -474,7 +479,13 @@ func getOffer(header []byte, isAccepted func(spec, offer string, specParams head
}
// Add to accepted types
acceptedTypes = append(acceptedTypes, acceptedType{utils.UnsafeString(spec), quality, specificity, order, params})
acceptedTypes = append(acceptedTypes, acceptedType{
spec: utils.UnsafeString(spec),
quality: quality,
specificity: specificity,
order: order,
params: params,
})
})
if len(acceptedTypes) > 1 {
@ -483,30 +494,24 @@ func getOffer(header []byte, isAccepted func(spec, offer string, specParams head
}
// Find the first offer that matches the accepted types
ret := ""
done := false
for _, acceptedType := range acceptedTypes {
if !done {
for _, offer := range offers {
if offer == "" {
continue
}
if isAccepted(acceptedType.spec, offer, acceptedType.params) {
ret = offer
done = true
break
for _, offer := range offers {
if offer == "" {
continue
}
if isAccepted(acceptedType.spec, offer, acceptedType.params) {
if acceptedType.params != nil {
headerParamPool.Put(acceptedType.params)
}
return offer
}
}
if acceptedType.params != nil {
for p := range acceptedType.params {
delete(acceptedType.params, p)
}
headerParamPool.Put(acceptedType.params)
}
}
return ret
return ""
}
// sortAcceptedTypes sorts accepted types by quality and specificity, preserving order of equal elements
@ -761,6 +766,7 @@ func genericParseBool[V GenericType](str string, parser func(bool) V, defaultVal
return genericParseDefault[V](err, func() V { return parser(result) }, defaultValue...)
}
//nolint:gosec // Casting in this function is not a concern
func genericParseType[V GenericType](str string, v V, defaultValue ...V) V {
switch any(v).(type) {
case int:

View File

@ -5,7 +5,6 @@
package fiber
import (
"fmt"
"strings"
"testing"
"time"
@ -138,6 +137,8 @@ func Benchmark_Utils_GetOffer(b *testing.B) {
},
}
b.ReportAllocs()
b.ResetTimer()
for _, tc := range testCases {
accept := []byte(tc.accept)
b.Run(tc.description, func(b *testing.B) {
@ -205,6 +206,8 @@ func Benchmark_Utils_ParamsMatch(b *testing.B) {
"appLe": []byte("orange"),
"param": []byte("foo"),
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
match = paramsMatch(specParams, `;param=foo; apple=orange`)
}
@ -317,6 +320,8 @@ func Benchmark_Utils_GetSplicedStrList(b *testing.B) {
destination := make([]string, 5)
result := destination
const input = `deflate, gzip,br,brotli,zstd`
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
result = getSplicedStrList(input, destination)
}
@ -359,6 +364,8 @@ func Test_Utils_SortAcceptedTypes(t *testing.T) {
// go test -v -run=^$ -bench=Benchmark_Utils_SortAcceptedTypes_Sorted -benchmem -count=4
func Benchmark_Utils_SortAcceptedTypes_Sorted(b *testing.B) {
acceptedTypes := make([]acceptedType, 3)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
acceptedTypes[0] = acceptedType{spec: "text/html", quality: 1, specificity: 1, order: 0}
acceptedTypes[1] = acceptedType{spec: "text/*", quality: 0.5, specificity: 1, order: 1}
@ -373,6 +380,8 @@ func Benchmark_Utils_SortAcceptedTypes_Sorted(b *testing.B) {
// go test -v -run=^$ -bench=Benchmark_Utils_SortAcceptedTypes_Unsorted -benchmem -count=4
func Benchmark_Utils_SortAcceptedTypes_Unsorted(b *testing.B) {
acceptedTypes := make([]acceptedType, 11)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
acceptedTypes[0] = acceptedType{spec: "text/html", quality: 1, specificity: 3, order: 0}
acceptedTypes[1] = acceptedType{spec: "text/*", quality: 0.5, specificity: 2, order: 1}
@ -452,9 +461,10 @@ func Test_Utils_getGroupPath(t *testing.T) {
}
// go test -v -run=^$ -bench=Benchmark_Utils_ -benchmem -count=3
func Benchmark_Utils_getGroupPath(b *testing.B) {
var res string
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = getGroupPath("/v1/long/path/john/doe", "/why/this/name/is/so/awesome")
_ = getGroupPath("/v1", "/")
@ -467,7 +477,8 @@ func Benchmark_Utils_getGroupPath(b *testing.B) {
func Benchmark_Utils_Unescape(b *testing.B) {
unescaped := ""
dst := make([]byte, 0)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
source := "/cr%C3%A9er"
pathBytes := utils.UnsafeBytes(source)
@ -483,9 +494,9 @@ func Test_Utils_Parse_Address(t *testing.T) {
testCases := []struct {
addr, host, port string
}{
{"[::1]:3000", "[::1]", "3000"},
{"127.0.0.1:3000", "127.0.0.1", "3000"},
{"/path/to/unix/socket", "/path/to/unix/socket", ""},
{addr: "[::1]:3000", host: "[::1]", port: "3000"},
{addr: "127.0.0.1:3000", host: "127.0.0.1", port: "3000"},
{addr: "/path/to/unix/socket", host: "/path/to/unix/socket", port: ""},
}
for _, c := range testCases {
@ -509,26 +520,27 @@ func Test_Utils_IsNoCache(t *testing.T) {
string
bool
}{
{"public", false},
{"no-cache", true},
{"public, no-cache, max-age=30", true},
{"public,no-cache", true},
{"public,no-cacheX", false},
{"no-cache, public", true},
{"Xno-cache, public", false},
{"max-age=30, no-cache,public", true},
{string: "public", bool: false},
{string: "no-cache", bool: true},
{string: "public, no-cache, max-age=30", bool: true},
{string: "public,no-cache", bool: true},
{string: "public,no-cacheX", bool: false},
{string: "no-cache, public", bool: true},
{string: "Xno-cache, public", bool: false},
{string: "max-age=30, no-cache,public", bool: true},
}
for _, c := range testCases {
ok := isNoCache(c.string)
require.Equal(t, c.bool, ok,
fmt.Sprintf("want %t, got isNoCache(%s)=%t", c.bool, c.string, ok))
require.Equal(t, c.bool, ok, "want %t, got isNoCache(%s)=%t", c.bool, c.string, ok)
}
}
// go test -v -run=^$ -bench=Benchmark_Utils_IsNoCache -benchmem -count=4
func Benchmark_Utils_IsNoCache(b *testing.B) {
var ok bool
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = isNoCache("public")
_ = isNoCache("no-cache")
@ -544,7 +556,10 @@ func Benchmark_Utils_IsNoCache(b *testing.B) {
func Benchmark_SlashRecognition(b *testing.B) {
search := "wtf/1234"
var result bool
b.Run("indexBytes", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
result = false
for i := 0; i < b.N; i++ {
if strings.IndexByte(search, slashDelimiter) != -1 {
@ -554,6 +569,8 @@ func Benchmark_SlashRecognition(b *testing.B) {
require.True(b, result)
})
b.Run("forEach", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
result = false
c := int32(slashDelimiter)
for i := 0; i < b.N; i++ {
@ -567,6 +584,8 @@ func Benchmark_SlashRecognition(b *testing.B) {
require.True(b, result)
})
b.Run("IndexRune", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
result = false
c := int32(slashDelimiter)
for i := 0; i < b.N; i++ {

View File

@ -10,14 +10,14 @@ import (
)
type Storage struct {
sync.RWMutex
data map[string]item // data
sync.RWMutex
}
type item struct {
v any // val
// max value is 4294967295 -> Sun Feb 07 2106 06:28:15 GMT+0000
e uint32 // exp
v any // val
}
func New() *Storage {
@ -46,7 +46,7 @@ func (s *Storage) Set(key string, val any, ttl time.Duration) {
if ttl > 0 {
exp = uint32(ttl.Seconds()) + utils.Timestamp()
}
i := item{exp, val}
i := item{e: exp, v: val}
s.Lock()
s.data[key] = i
s.Unlock()

View File

@ -26,10 +26,10 @@ func newCache() *cache {
// cache caches meta-data about a struct.
type cache struct {
l sync.RWMutex
m map[reflect.Type]*structInfo
regconv map[reflect.Type]Converter
tag string
l sync.RWMutex
}
// registerConverter registers a converter function for a custom type.

View File

@ -462,10 +462,10 @@ type unmarshaler struct {
// ConversionError stores information about a failed conversion.
type ConversionError struct {
Key string // key from the source map.
Type reflect.Type // expected type of elem
Index int // index for multi-value fields; -1 for single-value fields.
Err error // low-level error (when it exists)
Key string // key from the source map.
Index int // index for multi-value fields; -1 for single-value fields.
}
func (e ConversionError) Error() string {

View File

@ -11,10 +11,10 @@ import (
// Storage interface that is implemented by storage providers
type Storage struct {
mux sync.RWMutex
db map[string]entry
gcInterval time.Duration
done chan struct{}
gcInterval time.Duration
mux sync.RWMutex
}
type entry struct {
@ -69,7 +69,7 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
expire = uint32(exp.Seconds()) + utils.Timestamp()
}
e := entry{val, expire}
e := entry{data: val, expiry: expire}
s.mux.Lock()
s.db[key] = e
s.mux.Unlock()

View File

@ -40,6 +40,36 @@ const (
//
// TODO: Add timeout for graceful shutdown.
type ListenConfig struct {
// GracefulContext is a field to shutdown Fiber by given context gracefully.
//
// Default: nil
GracefulContext context.Context `json:"graceful_context"` //nolint:containedctx // It's needed to set context inside Listen.
// TLSConfigFunc allows customizing tls.Config as you want.
//
// Default: nil
TLSConfigFunc func(tlsConfig *tls.Config) `json:"tls_config_func"`
// ListenerFunc allows accessing and customizing net.Listener.
//
// Default: nil
ListenerAddrFunc func(addr net.Addr) `json:"listener_addr_func"`
// BeforeServeFunc allows customizing and accessing fiber app before serving the app.
//
// Default: nil
BeforeServeFunc func(app *App) error `json:"before_serve_func"`
// OnShutdownError allows to customize error behavior when to graceful shutdown server by given signal.
//
// Print error with log.Fatalf() by default.
// Default: nil
OnShutdownError func(err error)
// OnShutdownSuccess allows to customize success behavior when to graceful shutdown server by given signal.
//
// Default: nil
OnShutdownSuccess func()
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)
// WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chosen.
//
@ -64,26 +94,6 @@ type ListenConfig struct {
// Default : ""
CertClientFile string `json:"cert_client_file"`
// GracefulContext is a field to shutdown Fiber by given context gracefully.
//
// Default: nil
GracefulContext context.Context `json:"graceful_context"` //nolint:containedctx // It's needed to set context inside Listen.
// TLSConfigFunc allows customizing tls.Config as you want.
//
// Default: nil
TLSConfigFunc func(tlsConfig *tls.Config) `json:"tls_config_func"`
// ListenerFunc allows accessing and customizing net.Listener.
//
// Default: nil
ListenerAddrFunc func(addr net.Addr) `json:"listener_addr_func"`
// BeforeServeFunc allows customizing and accessing fiber app before serving the app.
//
// Default: nil
BeforeServeFunc func(app *App) error `json:"before_serve_func"`
// When set to true, it will not print out the «Fiber» ASCII art and listening address.
//
// Default: false
@ -98,17 +108,6 @@ type ListenConfig struct {
//
// Default: false
EnablePrintRoutes bool `json:"enable_print_routes"`
// OnShutdownError allows to customize error behavior when to graceful shutdown server by given signal.
//
// Print error with log.Fatalf() by default.
// Default: nil
OnShutdownError func(err error)
// OnShutdownSuccess allows to customize success behavior when to graceful shutdown server by given signal.
//
// Default: nil
OnShutdownSuccess func()
}
// listenConfigDefault is a function to set default values of ListenConfig.
@ -352,7 +351,7 @@ func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenC
}
fmt.Fprintf(out, "%s\n", fmt.Sprintf(figletFiberText, colors.Red+"v"+Version+colors.Reset)) //nolint:errcheck,revive // ignore error
fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") //nolint:errcheck,revive // ignore error
fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") //nolint:errcheck,revive,govet // ignore error
if host == "0.0.0.0" {
//nolint:errcheck,revive // ignore error

View File

@ -79,10 +79,10 @@ func Test_Listen_Graceful_Shutdown(t *testing.T) {
}
testCases := []struct {
Time time.Duration
ExpectedBody string
ExpectedStatusCode int
ExpectedErr error
ExpectedBody string
Time time.Duration
ExpectedStatusCode int
}{
{Time: 500 * time.Millisecond, ExpectedBody: "example.com", ExpectedStatusCode: StatusOK, ExpectedErr: nil},
{Time: 3 * time.Second, ExpectedBody: "", ExpectedStatusCode: StatusOK, ExpectedErr: errors.New("InmemoryListener is already closed: use of closed network connection")},

View File

@ -121,11 +121,11 @@ func Test_CtxLogger(t *testing.T) {
func Test_LogfKeyAndValues(t *testing.T) {
tests := []struct {
name string
level Level
format string
wantOutput string
fmtArgs []any
keysAndValues []any
wantOutput string
level Level
}{
{
name: "test logf with debug level and key-values",
@ -310,9 +310,9 @@ func Test_Tracew(t *testing.T) {
func Benchmark_LogfKeyAndValues(b *testing.B) {
tests := []struct {
name string
level Level
format string
keysAndValues []any
level Level
}{
{
name: "test logf with debug level and key-values",
@ -368,9 +368,9 @@ func Benchmark_LogfKeyAndValues(b *testing.B) {
func Benchmark_LogfKeyAndValues_Parallel(b *testing.B) {
tests := []struct {
name string
level Level
format string
keysAndValues []any
level Level
}{
{
name: "debug level with key-values",

View File

@ -1,141 +0,0 @@
# Adaptor
![Release](https://img.shields.io/github/release/gofiber/adaptor.svg)
[![Discord](https://img.shields.io/badge/discord-join%20channel-7289DA)](https://gofiber.io/discord)
![Test](https://github.com/gofiber/adaptor/workflows/Test/badge.svg)
![Security](https://github.com/gofiber/adaptor/workflows/Security/badge.svg)
![Linter](https://github.com/gofiber/adaptor/workflows/Linter/badge.svg)
Converter for net/http handlers to/from Fiber request handlers, special thanks to [@arsmn](https://github.com/arsmn)!
### Install
```
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/adaptor/v2
```
### Functions
| Name | Signature | Description
| :--- | :--- | :---
| HTTPHandler | `HTTPHandler(h http.Handler) fiber.Handler` | http.Handler -> fiber.Handler
| HTTPHandlerFunc | `HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler` | http.HandlerFunc -> fiber.Handler
| HTTPMiddleware | `HTTPHandlerFunc(mw func(http.Handler) http.Handler) fiber.Handler` | func(http.Handler) http.Handler -> fiber.Handler
| 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
### net/http to Fiber
```go
package main
import (
"fmt"
"net/http"
"github.com/gofiber/adaptor/v2"
"github.com/gofiber/fiber/v3"
)
func main() {
// New fiber app
app := fiber.New()
// http.Handler -> fiber.Handler
app.Get("/", adaptor.HTTPHandler(handler(greet)))
// http.HandlerFunc -> fiber.Handler
app.Get("/func", adaptor.HTTPHandlerFunc(greet))
// Listen on port 3000
app.Listen(":3000")
}
func handler(f http.HandlerFunc) http.Handler {
return http.HandlerFunc(f)
}
func greet(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World!")
}
```
### net/http middleware to Fiber
```go
package main
import (
"log"
"net/http"
"github.com/gofiber/adaptor/v2"
"github.com/gofiber/fiber/v3"
)
func main() {
// New fiber app
app := fiber.New()
// http middleware -> fiber.Handler
app.Use(adaptor.HTTPMiddleware(logMiddleware))
// Listen on port 3000
app.Listen(":3000")
}
func logMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("log middleware")
next.ServeHTTP(w, r)
})
}
```
### Fiber Handler to net/http
```go
package main
import (
"net/http"
"github.com/gofiber/adaptor/v2"
"github.com/gofiber/fiber/v3"
)
func main() {
// fiber.Handler -> http.Handler
http.Handle("/", adaptor.FiberHandler(greet))
// fiber.Handler -> http.HandlerFunc
http.HandleFunc("/func", adaptor.FiberHandlerFunc(greet))
// Listen on port 3000
http.ListenAndServe(":3000", nil)
}
func greet(c fiber.Ctx) error {
return c.SendString("Hello World!")
}
```
### Fiber App to net/http
```go
package main
import (
"github.com/gofiber/adaptor/v2"
"github.com/gofiber/fiber/v3"
"net/http"
)
func main() {
app := fiber.New()
app.Get("/greet", greet)
// Listen on port 3000
http.ListenAndServe(":3000", adaptor.FiberApp(app))
}
func greet(c fiber.Ctx) error {
return c.SendString("Hello World!")
}
```

Some files were not shown because too many files have changed in this diff Show More