158 lines
3.3 KiB
Go
158 lines
3.3 KiB
Go
package router
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"regexp"
|
|
)
|
|
|
|
type HandlerFunc func(ctx *Context)
|
|
|
|
func (handlerFunc HandlerFunc) ServeHTTP(ctx *Context) {
|
|
handlerFunc(ctx)
|
|
}
|
|
|
|
type Handler interface {
|
|
ServeHTTP(ctx *Context)
|
|
}
|
|
|
|
type Router struct {
|
|
routingHandler *RoutingHandler
|
|
headHandler Handler
|
|
}
|
|
|
|
func NewRouter() *Router {
|
|
rh := NewRoutingHandler()
|
|
return &Router{
|
|
headHandler: rh,
|
|
routingHandler: rh,
|
|
}
|
|
}
|
|
|
|
func (rt *Router) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
|
|
ctx := &Context{
|
|
Writer: writer,
|
|
Req: req,
|
|
PathMap: make(map[string]string),
|
|
StatusCode: http.StatusOK,
|
|
}
|
|
rt.headHandler.ServeHTTP(ctx)
|
|
}
|
|
|
|
func (rout *Router) Use(mw func(next Handler) Handler) {
|
|
rout.headHandler = mw(rout.headHandler)
|
|
}
|
|
|
|
func (rout *Router) AddRoute(method, path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute(method, path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Get(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("GET", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Head(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("HEAD", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Post(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("POST", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Put(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("PUT", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Patch(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("PATCH", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Delete(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("DELETE", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) Option(path string, handlerFunc HandlerFunc) {
|
|
rout.routingHandler.AddRoute("OPTION", path, handlerFunc)
|
|
}
|
|
|
|
func (rout *Router) NotFound(handlerFunc HandlerFunc) {
|
|
rout.routingHandler.notFound = handlerFunc
|
|
}
|
|
|
|
func (rout *Router) ListRoutes() []*Route {
|
|
return rout.routingHandler.Routes
|
|
}
|
|
|
|
type RoutingHandler struct {
|
|
Routes []*Route
|
|
notFound Handler
|
|
}
|
|
|
|
func NewRoutingHandler() *RoutingHandler {
|
|
notFound := HandlerFunc(func(ctx *Context) {
|
|
http.NotFound(ctx.Writer, ctx.Req)
|
|
})
|
|
return &RoutingHandler{
|
|
notFound: notFound,
|
|
}
|
|
}
|
|
|
|
const globalRoutePattern = `^%s$`
|
|
|
|
func (hand *RoutingHandler) AddRoute(method, path string, handlerFunc HandlerFunc) error {
|
|
var err error
|
|
path = pathComp(path)
|
|
path = fmt.Sprintf(globalRoutePattern, path)
|
|
re, err := regexp.Compile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
route := &Route{
|
|
Method: method,
|
|
Path: path,
|
|
Regexp: re,
|
|
Handler: handlerFunc,
|
|
}
|
|
hand.Routes = append(hand.Routes, route)
|
|
return err
|
|
}
|
|
|
|
func (hand *RoutingHandler) ServeHTTP(ctx *Context) {
|
|
handler := hand.notFound
|
|
for _, route := range hand.Routes {
|
|
match := route.Match(ctx.Req)
|
|
if !match {
|
|
continue
|
|
}
|
|
pathMatch := route.Regexp.FindStringSubmatch(ctx.Req.URL.Path)
|
|
subNames := route.Regexp.SubexpNames()
|
|
for i, val := range pathMatch {
|
|
key := subNames[i]
|
|
if key != "" {
|
|
ctx.PathMap[key] = val
|
|
}
|
|
}
|
|
handler = route.Handler
|
|
break
|
|
}
|
|
handler.ServeHTTP(ctx)
|
|
}
|
|
|
|
type Route struct {
|
|
Method string
|
|
Path string
|
|
Regexp *regexp.Regexp
|
|
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 false
|
|
}
|
|
return true
|
|
}
|