1 Star 0 Fork 0

raininfall/router

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
router.go 7.63 KB
一键复制 编辑 原始数据 按行查看 历史
Songrq 提交于 2019-10-24 15:23 +08:00 . Init
// Copyright 2018 The xujiajun/gorouter Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package router
import (
"errors"
"net/http"
"regexp"
"strings"
"gitee.com/raininfall/parameters"
)
var (
// ErrGenerateParameters is returned when generating a route with wrong parameters.
ErrGenerateParameters = errors.New("params contains wrong parameters")
// ErrNotFoundRoute is returned when generating a route that can not find route in tree.
ErrNotFoundRoute = errors.New("cannot find route in tree")
// ErrNotFoundMethod is returned when generating a route that can not find method in tree.
ErrNotFoundMethod = errors.New("cannot find method in tree")
// ErrPatternGrammar is returned when generating a route that pattern grammar error.
ErrPatternGrammar = errors.New("pattern grammar error")
defaultPattern = `[\w]+`
idPattern = `[\d]+`
idKey = `id`
)
type (
// MiddlewareType is a public type that is used for middleware
MiddlewareType func(next HandlerFunc) HandlerFunc
// Router is a simple HTTP route multiplexer that parses a request path,
// records any URL params, and executes an end handler.
Router struct {
prefix string
// The middleware stack
middleware []MiddlewareType
// the tree routers
trees map[string]*Tree
parameters Parameters
// Custom route not found handler
notFound http.HandlerFunc
// PanicHandler for handling panic.
PanicHandler func(ctx Context, err interface{})
}
// Parameters records some parameters
Parameters struct {
routeName string
}
)
// New returns a newly initialized Router object that implements the Router
func New() *Router {
return &Router{
trees: make(map[string]*Tree),
}
}
// Group define routes groups if there is a path prefix that uses `prefix`
func (r *Router) Group(prefix string) *Router {
return &Router{
prefix: prefix,
trees: r.trees,
middleware: r.middleware,
}
}
// Generate returns reverse routing by method, routeName and params
func (r *Router) Generate(method string, routeName string, params map[string]string) (string, error) {
tree, ok := r.trees[method]
if !ok {
return "", ErrNotFoundMethod
}
route, ok := tree.routes[routeName]
if !ok {
return "", ErrNotFoundRoute
}
var segments []string
res := splitPattern(route.path)
for _, segment := range res {
if string(segment[0]) == ":" {
key := params[string(segment[1:])]
re := regexp.MustCompile(defaultPattern)
if one := re.Find([]byte(key)); one == nil {
return "", ErrGenerateParameters
}
segments = append(segments, key)
continue
}
if string(segment[0]) == "{" {
segmentLen := len(segment)
if string(segment[segmentLen-1]) == "}" {
splitRes := strings.Split(string(segment[1:segmentLen-1]), ":")
re := regexp.MustCompile(splitRes[1])
key := params[splitRes[0]]
if one := re.Find([]byte(key)); one == nil {
return "", ErrGenerateParameters
}
segments = append(segments, key)
continue
}
return "", ErrPatternGrammar
}
if string(segment[len(segment)-1]) == "}" && string(segment[0]) != "{" {
return "", ErrPatternGrammar
}
segments = append(segments, segment)
continue
}
return "/" + strings.Join(segments, "/"), nil
}
// NotFoundFunc registers a handler when the request route is not found
func (r *Router) NotFoundFunc(handler http.HandlerFunc) {
r.notFound = handler
}
// Handle registers a new request handler with the given path and method.
func (r *Router) Handle(method string, path string, handle HandlerFunc) {
tree, ok := r.trees[method]
if !ok {
tree = NewTree()
r.trees[method] = tree
}
if r.prefix != "" {
path = r.prefix + "/" + path
}
if routeName := r.parameters.routeName; routeName != "" {
tree.parameters.routeName = routeName
}
tree.Add(path, handle, r.middleware...)
}
// GetParam returns route param stored in http.request.
func GetParam(r *http.Request, key string) string {
return GetAllParams(r)[key]
}
// contextKeyType is a private struct that is used for storing values in net.Context
type contextKeyType struct{}
// contextKey is the key that is used to store values in net.Context for each request
var contextKey = contextKeyType{}
// ParamsMapType is a private type that is used to store route params
type ParamsMapType map[string]string
// GetAllParams returns all route params stored in http.Request.
func GetAllParams(r *http.Request) ParamsMapType {
if values, ok := r.Context().Value(contextKey).(ParamsMapType); ok {
return values
}
return nil
}
// Serve makes the router implement the router.Context interface.
func (r *Router) Serve(inputContext InputContext) bool {
ctx := &routerContext{InputContext: inputContext}
requestURL := ctx.Path()
if r.PanicHandler != nil {
defer func() {
if err := recover(); err != nil {
r.PanicHandler(ctx, err)
}
}()
}
if _, ok := r.trees[ctx.Method()]; !ok {
return false
}
nodes := r.trees[ctx.Method()].Find(requestURL, false)
if len(nodes) > 0 {
node := nodes[0]
if node.handle != nil {
if node.path == requestURL {
handle(ctx, node.handle, node.middleware)
return true
}
if node.path == requestURL[1:] {
handle(ctx, node.handle, node.middleware)
return true
}
}
}
if len(nodes) == 0 {
res := strings.Split(requestURL, "/")
prefix := res[1]
nodes := r.trees[ctx.Method()].Find(prefix, true)
for _, node := range nodes {
if handler := node.handle; handler != nil && node.path != requestURL {
if matchParamsMap, ok := r.matchAndParse(requestURL, node.path); ok {
ctx.Parameters = matchParamsMap
handle(ctx, handler, node.middleware)
return true
}
}
}
}
return false
}
// Use appends a middleware handler to the middleware stack.
func (r *Router) Use(middleware ...MiddlewareType) {
if len(middleware) > 0 {
r.middleware = append(r.middleware, middleware...)
}
}
// handle executes middleware chain
func handle(ctx Context, handler HandlerFunc, middleware []MiddlewareType) {
baseHandler := handler
for i := len(middleware) - 1; i >= 0; i-- {
baseHandler = middleware[i](baseHandler)
}
baseHandler(ctx)
}
// Match checks if the request matches the route pattern
func (r *Router) Match(requestURL string, path string) bool {
_, ok := r.matchAndParse(requestURL, path)
return ok
}
// matchAndParse checks if the request matches the route path and returns a map of the parsed
func (r *Router) matchAndParse(requestURL string, path string) (matchParams parameters.Parameters, b bool) {
var (
matchName []string
pattern string
)
b = true
matchParams = parameters.New()
res := strings.Split(path, "/")
for _, str := range res {
if str == "" {
continue
}
strLen := len(str)
firstChar := str[0]
lastChar := str[strLen-1]
if string(firstChar) == "{" && string(lastChar) == "}" {
matchStr := string(str[1 : strLen-1])
res := strings.Split(matchStr, ":")
matchName = append(matchName, res[0])
pattern = pattern + "/" + "(" + res[1] + ")"
} else if string(firstChar) == ":" {
matchStr := str
res := strings.Split(matchStr, ":")
matchName = append(matchName, res[1])
if res[1] == idKey {
pattern = pattern + "/" + "(" + idPattern + ")"
} else {
pattern = pattern + "/" + "(" + defaultPattern + ")"
}
} else {
pattern = pattern + "/" + str
}
}
if strings.HasSuffix(requestURL, "/") {
pattern = pattern + "/"
}
re := regexp.MustCompile(pattern)
if subMatch := re.FindSubmatch([]byte(requestURL)); subMatch != nil {
if string(subMatch[0]) == requestURL {
subMatch = subMatch[1:]
for k, v := range subMatch {
matchParams.Set(matchName[k], string(v))
}
return
}
}
return nil, false
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/raininfall/router.git
git@gitee.com:raininfall/router.git
raininfall
router
router
b1320ef91e8f

搜索帮助