mirror of
https://github.com/gofiber/fiber.git
synced 2025-02-22 02:32:43 +00:00
147 lines
4.1 KiB
Go
147 lines
4.1 KiB
Go
|
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
||
|
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
||
|
// 📌 API Documentation: https://docs.gofiber.io
|
||
|
|
||
|
package fiber
|
||
|
|
||
|
import (
|
||
|
"sort"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
)
|
||
|
|
||
|
// Put fields related to mounting.
|
||
|
type mountFields struct {
|
||
|
// Mounted and main apps
|
||
|
appList map[string]*App
|
||
|
// Ordered keys of apps (sorted by key length for Render)
|
||
|
appListKeys []string
|
||
|
// check added routes of sub-apps
|
||
|
subAppsRoutesAdded sync.Once
|
||
|
// Prefix of app if it was mounted
|
||
|
mountPath string
|
||
|
}
|
||
|
|
||
|
// Create empty mountFields instance
|
||
|
func newMountFields(app *App) *mountFields {
|
||
|
return &mountFields{
|
||
|
appList: map[string]*App{"": app},
|
||
|
appListKeys: make([]string, 0),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Mount attaches another app instance as a sub-router along a routing path.
|
||
|
// It's very useful to split up a large API as many independent routers and
|
||
|
// compose them as a single service using Mount. The fiber's error handler and
|
||
|
// any of the fiber's sub apps are added to the application's error handlers
|
||
|
// to be invoked on errors that happen within the prefix route.
|
||
|
func (app *App) Mount(prefix string, fiber *App) Router {
|
||
|
prefix = strings.TrimRight(prefix, "/")
|
||
|
if prefix == "" {
|
||
|
prefix = "/"
|
||
|
}
|
||
|
|
||
|
// Support for configs of mounted-apps and sub-mounted-apps
|
||
|
for mountedPrefixes, subApp := range fiber.mountFields.appList {
|
||
|
subApp.mountFields.mountPath = prefix + mountedPrefixes
|
||
|
app.mountFields.appList[prefix+mountedPrefixes] = subApp
|
||
|
}
|
||
|
|
||
|
// Execute onMount hooks
|
||
|
if err := fiber.hooks.executeOnMountHooks(app); err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
return app
|
||
|
}
|
||
|
|
||
|
// Mount attaches another app instance as a sub-router along a routing path.
|
||
|
// It's very useful to split up a large API as many independent routers and
|
||
|
// compose them as a single service using Mount.
|
||
|
func (grp *Group) Mount(prefix string, fiber *App) Router {
|
||
|
groupPath := getGroupPath(grp.Prefix, prefix)
|
||
|
groupPath = strings.TrimRight(groupPath, "/")
|
||
|
if groupPath == "" {
|
||
|
groupPath = "/"
|
||
|
}
|
||
|
|
||
|
// Support for configs of mounted-apps and sub-mounted-apps
|
||
|
for mountedPrefixes, subApp := range fiber.mountFields.appList {
|
||
|
subApp.mountFields.mountPath = groupPath + mountedPrefixes
|
||
|
grp.app.mountFields.appList[groupPath+mountedPrefixes] = subApp
|
||
|
}
|
||
|
|
||
|
// Execute onMount hooks
|
||
|
if err := fiber.hooks.executeOnMountHooks(grp.app); err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
return grp
|
||
|
}
|
||
|
|
||
|
// The MountPath property contains one or more path patterns on which a sub-app was mounted.
|
||
|
func (app *App) MountPath() string {
|
||
|
return app.mountFields.mountPath
|
||
|
}
|
||
|
|
||
|
// generateAppListKeys generates app list keys for Render, should work after appendSubAppLists
|
||
|
func (app *App) generateAppListKeys() {
|
||
|
for key := range app.mountFields.appList {
|
||
|
app.mountFields.appListKeys = append(app.mountFields.appListKeys, key)
|
||
|
}
|
||
|
|
||
|
sort.Slice(app.mountFields.appListKeys, func(i, j int) bool {
|
||
|
return len(app.mountFields.appListKeys[i]) < len(app.mountFields.appListKeys[j])
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// appendSubAppLists supports nested for sub apps
|
||
|
func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) {
|
||
|
for prefix, subApp := range appList {
|
||
|
// skip real app
|
||
|
if prefix == "" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if len(parent) > 0 {
|
||
|
prefix = parent[0] + prefix
|
||
|
}
|
||
|
|
||
|
if _, ok := app.mountFields.appList[prefix]; !ok {
|
||
|
app.mountFields.appList[prefix] = subApp
|
||
|
}
|
||
|
|
||
|
// The first element of appList is always the app itself. If there are no other sub apps, we should skip appending nested apps.
|
||
|
if len(subApp.mountFields.appList) > 1 {
|
||
|
app.appendSubAppLists(subApp.mountFields.appList, prefix)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// addSubAppsRoutes adds routes of sub apps nestedly when to start the server
|
||
|
func (app *App) addSubAppsRoutes(appList map[string]*App, parent ...string) {
|
||
|
for prefix, subApp := range appList {
|
||
|
// skip real app
|
||
|
if prefix == "" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if len(parent) > 0 {
|
||
|
prefix = parent[0] + prefix
|
||
|
}
|
||
|
|
||
|
// add routes
|
||
|
stack := subApp.stack
|
||
|
for m := range stack {
|
||
|
for r := range stack[m] {
|
||
|
route := app.copyRoute(stack[m][r])
|
||
|
app.addRoute(route.Method, app.addPrefixToRoute(prefix, route), true)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
atomic.AddUint32(&app.handlersCount, subApp.handlersCount)
|
||
|
}
|
||
|
}
|