1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-21 06:33:11 +00:00
fiber/router.go

202 lines
5.0 KiB
Go
Raw Normal View History

// 🔌 Fiber is an Express.js inspired web framework build on 🚀 Fasthttp.
2020-01-22 05:42:37 +01:00
// 📌 Please open an issue if you got suggestions or found a bug!
2020-01-16 05:58:23 +01:00
// 🖥 https://github.com/gofiber/fiber
2020-01-15 03:07:49 +01:00
2020-01-16 00:24:58 +01:00
// 🦸 Not all heroes wear capes, thank you to some amazing people
2020-01-15 03:07:49 +01:00
// 💖 @valyala, @dgrr, @erikdubbelboer, @savsgio, @julienschmidt
2019-12-30 07:29:42 -05:00
package fiber
import (
"regexp"
2020-01-21 00:57:10 +01:00
"strings"
2019-12-30 07:29:42 -05:00
"github.com/valyala/fasthttp"
)
// Route : struct
type Route struct {
2020-01-11 04:59:51 +01:00
// HTTP method in uppercase, can be a * for Use() & All()
2020-01-28 14:28:09 -05:00
Method string
// Stores the original path
2020-01-28 14:28:09 -05:00
Path string
// Bool that defines if the route is a Use() middleware
Midware bool
2020-01-15 03:07:49 +01:00
// wildcard bool is for routes without a path, * and /*
2020-01-28 14:28:09 -05:00
Wildcard bool
2020-01-11 04:59:51 +01:00
// Stores compiled regex special routes :params, *wildcards, optionals?
2020-01-28 14:28:09 -05:00
Regex *regexp.Regexp
2020-01-11 04:59:51 +01:00
// Store params if special routes :params, *wildcards, optionals?
2020-01-28 14:28:09 -05:00
Params []string
2020-01-11 04:59:51 +01:00
// Callback function for specific route
2020-01-28 14:28:09 -05:00
Handler func(*Ctx)
2019-12-30 07:29:42 -05:00
}
2020-01-16 02:31:44 +01:00
// Function to add a route correctly
func (r *Fiber) register(method string, args ...interface{}) {
2020-01-28 14:28:09 -05:00
// Set if method is Use() midware
var midware = method == "MIDWARE"
2020-01-21 00:57:10 +01:00
// Match any method
2020-01-28 14:28:09 -05:00
if method == "ALL" || midware {
2020-01-21 00:57:10 +01:00
method = "*"
}
2020-01-16 02:31:44 +01:00
// Prepare possible variables
var path string // We could have a path/prefix
var handler func(*Ctx) // We could have a ctx handler
2020-01-16 02:31:44 +01:00
// Only 1 argument, so no path/prefix
if len(args) == 1 {
2020-02-04 13:53:46 +01:00
// switch arg := args[0].(type) {
// case func(*Ctx):
// handler = arg
// case func(*fiber.Ctx):
// handler = arg
// default:
// panic("Invalid Context function")
// }
2020-01-16 02:31:44 +01:00
handler = args[0].(func(*Ctx))
} else if len(args) > 1 {
path = args[0].(string)
handler = args[1].(func(*Ctx))
if path[0] != '/' && path[0] != '*' {
panic("Invalid path, must begin with slash '/' or wildcard '*'")
}
}
2020-01-28 14:28:09 -05:00
if midware && strings.Contains(path, "/:") {
2020-01-21 00:57:10 +01:00
panic("You cannot use :params in Use()")
}
2020-01-21 00:57:10 +01:00
// If Use() path == "/", match anything aka *
2020-01-28 14:28:09 -05:00
if midware && path == "/" {
2020-01-21 00:57:10 +01:00
path = "*"
}
2020-01-16 00:24:58 +01:00
// If the route needs to match any path
2019-12-30 13:33:50 +01:00
if path == "" || path == "*" || path == "/*" {
r.routes = append(r.routes, &Route{method, path, midware, true, nil, nil, handler})
2019-12-30 13:33:50 +01:00
return
}
2019-12-30 13:33:50 +01:00
// Get params from path
params := getParams(path)
// If path has no params (simple path), we don't need regex (also for use())
2020-01-28 14:28:09 -05:00
if midware || len(params) == 0 {
r.routes = append(r.routes, &Route{method, path, midware, false, nil, nil, handler})
2019-12-30 13:33:50 +01:00
return
}
// We have parametes, so we need to compile regex from the path
2019-12-30 13:33:50 +01:00
regex, err := getRegex(path)
if err != nil {
panic("Invalid url pattern: " + path)
}
2020-01-16 00:24:58 +01:00
// Add regex + params to route
r.routes = append(r.routes, &Route{method, path, midware, false, regex, params, handler})
2019-12-30 13:33:50 +01:00
}
2020-01-11 04:59:51 +01:00
// then try to match a route as efficient as possible.
2019-12-30 13:33:50 +01:00
func (r *Fiber) handler(fctx *fasthttp.RequestCtx) {
2020-01-10 03:09:43 +01:00
found := false
2019-12-30 13:33:50 +01:00
// get custom context from sync pool
ctx := acquireCtx(fctx)
2019-12-30 13:33:50 +01:00
// get path and method from main context
path := ctx.Path()
method := ctx.Method()
2019-12-30 13:33:50 +01:00
// loop trough routes
for _, route := range r.routes {
// Skip route if method is not allowed
2020-01-28 14:28:09 -05:00
if route.Method != "*" && route.Method != method {
2019-12-30 13:33:50 +01:00
continue
}
2020-01-21 00:57:10 +01:00
// First check if we match a wildcard or static path
2020-01-28 14:28:09 -05:00
if route.Wildcard || route.Path == path {
2020-01-21 00:57:10 +01:00
// if route.wildcard || (route.path == path && route.params == nil) {
2019-12-30 13:33:50 +01:00
// If * always set the path to the wildcard parameter
2020-01-28 14:28:09 -05:00
if route.Wildcard {
2019-12-30 13:33:50 +01:00
ctx.params = &[]string{"*"}
ctx.values = []string{path}
}
2020-01-10 03:09:43 +01:00
found = true
2020-01-15 03:07:49 +01:00
// Set route pointer if user wants to call .Route()
ctx.route = route
2019-12-30 13:33:50 +01:00
// Execute handler with context
2020-01-28 14:28:09 -05:00
route.Handler(ctx)
2019-12-30 13:33:50 +01:00
// if next is not set, leave loop and release ctx
if !ctx.next {
break
}
// set next to false for next iteration
ctx.next = false
// continue to go to the next route
continue
}
2020-01-21 00:57:10 +01:00
// If route is Use() and path starts with route.path
// aka strings.HasPrefix(route.path, path)
2020-01-28 14:28:09 -05:00
if route.Midware && strings.HasPrefix(path, route.Path) {
2020-01-21 00:57:10 +01:00
found = true
ctx.route = route
2020-01-28 14:28:09 -05:00
route.Handler(ctx)
2020-01-21 00:57:10 +01:00
if !ctx.next {
break
}
ctx.next = false
continue
}
2020-01-11 04:59:51 +01:00
// Skip route if regex does not exist
2020-01-28 14:28:09 -05:00
if route.Regex == nil {
2020-01-11 04:59:51 +01:00
continue
}
2019-12-30 13:33:50 +01:00
// Skip route if regex does not match
2020-01-28 14:28:09 -05:00
if !route.Regex.MatchString(path) {
2019-12-30 07:29:42 -05:00
continue
}
2019-12-30 07:29:42 -05:00
// If we have parameters, lets find the matches
2020-01-28 14:28:09 -05:00
if len(route.Params) > 0 {
matches := route.Regex.FindAllStringSubmatch(path, -1)
2019-12-30 07:29:42 -05:00
// If we have matches, add params and values to context
if len(matches) > 0 && len(matches[0]) > 1 {
2020-01-28 14:28:09 -05:00
ctx.params = &route.Params
2019-12-30 07:29:42 -05:00
ctx.values = matches[0][1:len(matches[0])]
}
}
2020-01-10 03:09:43 +01:00
found = true
2020-01-15 03:07:49 +01:00
// Set route pointer if user wants to call .Route()
ctx.route = route
2019-12-30 07:29:42 -05:00
// Execute handler with context
2020-01-28 14:28:09 -05:00
route.Handler(ctx)
2019-12-30 07:29:42 -05:00
// if next is not set, leave loop and release ctx
if !ctx.next {
break
}
2019-12-30 07:29:42 -05:00
// set next to false for next iteration
ctx.next = false
}
2020-01-11 04:59:51 +01:00
// No routes found
2020-01-10 03:09:43 +01:00
if !found {
2020-01-11 04:59:51 +01:00
// Custom 404 handler?
2020-01-10 03:09:43 +01:00
ctx.Status(404).Send("Not Found")
}
2019-12-30 07:29:42 -05:00
// release context back into sync pool
releaseCtx(ctx)
}