151 lines
3.0 KiB
Go
151 lines
3.0 KiB
Go
package router
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"regexp"
|
|
)
|
|
|
|
type Handler interface {
|
|
ServeHTTP(rctx *Context)
|
|
}
|
|
|
|
type HandlerFunc func(rctx *Context)
|
|
|
|
func (handlerFunc HandlerFunc) ServeHTTP(rctx *Context) {
|
|
handlerFunc(rctx)
|
|
}
|
|
|
|
type Router struct {
|
|
routeHandler *Selector
|
|
}
|
|
|
|
func NewRouter() *Router {
|
|
return &Router{
|
|
routeHandler: NewSelector(),
|
|
}
|
|
}
|
|
|
|
func (rout *Router) Selector() *Selector {
|
|
return rout.routeHandler
|
|
}
|
|
|
|
func (rout *Router) AddRoute(method, path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute(method, path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Head(path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute("HEAD", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Get(path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute("GET", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Post(path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute("POST", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Put(path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute("PUT", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Patch(path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute("PATCH", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Delete(path string, handlerFunc HandlerFunc) {
|
|
rout.routeHandler.AddRoute("DELETE", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
|
|
rctx := NewContext(writer, req)
|
|
rout.routeHandler.ServeHTTP(rctx)
|
|
}
|
|
|
|
func (rout *Router) NotFound(handlerFunc HandlerFunc) {
|
|
rout.routeHandler.notFound = handlerFunc
|
|
}
|
|
|
|
type Selector struct {
|
|
Routes []*Route
|
|
notFound Handler
|
|
}
|
|
|
|
func NewSelector() *Selector {
|
|
notFound := HandlerFunc(func(ctx *Context) {
|
|
http.NotFound(ctx.Writer, ctx.Request)
|
|
})
|
|
return &Selector{
|
|
Routes: make([]*Route, 0),
|
|
notFound: notFound,
|
|
}
|
|
}
|
|
|
|
const globalRoutePattern = `^%s$`
|
|
|
|
func (hand *Selector) AddRoute(method, path string, handlerFunc HandlerFunc) error {
|
|
var err error
|
|
rawPath := path
|
|
path, err = pathCompiler(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
path = fmt.Sprintf(globalRoutePattern, path)
|
|
re, err := regexp.Compile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
route := &Route{
|
|
Method: method,
|
|
Path: path,
|
|
RawPath: rawPath,
|
|
Handler: handlerFunc,
|
|
Regexp: re,
|
|
}
|
|
hand.Routes = append(hand.Routes, route)
|
|
return err
|
|
}
|
|
|
|
func (hand *Selector) ServeHTTP(rctx *Context) {
|
|
realHandler := hand.notFound
|
|
for _, route := range hand.Routes {
|
|
match := route.Match(rctx.Request)
|
|
if !match {
|
|
continue
|
|
}
|
|
subvals := route.Regexp.FindStringSubmatch(rctx.Request.URL.Path)
|
|
subkeys := route.Regexp.SubexpNames()
|
|
for i, val := range subvals {
|
|
key := subkeys[i]
|
|
if key != "" {
|
|
rctx.PathMap[key] = val
|
|
}
|
|
}
|
|
realHandler = route.Handler
|
|
break
|
|
}
|
|
realHandler.ServeHTTP(rctx)
|
|
}
|
|
|
|
type Route struct {
|
|
Method string
|
|
Regexp *regexp.Regexp
|
|
Path string
|
|
RawPath string
|
|
Handler HandlerFunc
|
|
}
|
|
|
|
func (route Route) Match(req *http.Request) bool {
|
|
if req.Method != route.Method {
|
|
return false
|
|
}
|
|
match := route.Regexp.MatchString(req.URL.Path)
|
|
if match {
|
|
return true
|
|
}
|
|
return false
|
|
}
|