package server import ( "context" "fmt" "net/http" "sort" "strings" "git.pyer.club/kingecg/gologger" ) type RequestCtxKey string type Route struct { Method string Path string Handler http.Handler matcher *UrlParamMatcher middles *MiddlewareLink } func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) { if route.middles.Len() > 0 && !route.middles.ServeHTTP(w, r) { return } route.Handler.ServeHTTP(w, r) } func (route *Route) Match(r *http.Request) bool { l := gologger.GetLogger("Route") l.Debug(fmt.Sprintf("match route: %s %s", r.Method, r.URL.Path)) if route.Method != "" && route.Method != r.Method { l.Debug("method not match") return false } if route.matcher != nil && route.matcher.Reg != nil { params, matched := MatchUrlParam(r.URL.Path, route.matcher) if matched { ctx := r.Context() data := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) for _, p := range route.matcher.Params { data[p] = params[p] } return true } l.Debug("Not match matcher reg") return false } if route.Path == "" { return true } return strings.HasPrefix(r.URL.Path, route.Path) } func (route *Route) Add(m Middleware) { route.middles.Add(m) } // NewRoute 返回一个新的Route实例 // 参数: // - method: 请求方法 // - path: 请求路径 // - handleFn: http.Handler处理函数 // 返回值: // - *Route: 一个指向Route的指针 func NewRoute(method string, path string, handleFn http.Handler) *Route { ret := &Route{ Method: method, Path: path, middles: NewMiddlewareLink(), } p := ParseUrl(path) // 使用handleFn构建handler ret.Handler = handleFn ret.matcher = &p return ret } type Routes []*Route func (rs Routes) Less(i, j int) bool { iPaths := strings.Split(rs[i].Path, "/") jPaths := strings.Split(rs[j].Path, "/") if len(iPaths) > len(jPaths) { return true } if len(iPaths) < len(jPaths) { return false } for k := 0; k < len(iPaths); k++ { if iPaths[k] != jPaths[k] { return iPaths[k] < jPaths[k] } } return false } func (rs Routes) Len() int { return len(rs) } func (rs Routes) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] } // 可以嵌套的Rest http server mux type RestMux struct { Path string routes Routes middlewares *MiddlewareLink } func (mux *RestMux) Use(m Middleware) { mux.middlewares.Add(m) } func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { c := r.Context() data := map[string]interface{}{} cn := context.WithValue(c, RequestCtxKey("data"), data) newRequest := r.WithContext(cn) if mux.middlewares.Len() > 0 && !mux.middlewares.ServeHTTP(w, newRequest) { return } // 根据r 从routes中找到匹配的路由 for _, route := range mux.routes { if route.Match(newRequest) { route.ServeHTTP(w, newRequest) return } } http.NotFound(w, r) } func (mux *RestMux) HandleFunc(method string, path string, f func(http.ResponseWriter, *http.Request)) *Route { r := NewRoute(method, path, http.HandlerFunc(f)) mux.routes = append(mux.routes, r) sort.Sort(mux.routes) return r } func (mux *RestMux) Get(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("GET", path, f) } func (mux *RestMux) Post(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("POST", path, f) } func (mux *RestMux) Put(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("PUT", path, f) } func (mux *RestMux) Delete(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("DELETE", path, f) } func (mux *RestMux) Patch(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("PATCH", path, f) } func (mux *RestMux) Head(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("HEAD", path, f) } func (mux *RestMux) Option(path string, f func(http.ResponseWriter, *http.Request)) *Route { return mux.HandleFunc("OPTION", path, f) } func (mux *RestMux) HandleMux(nmux *RestMux) *Route { p := nmux.Path if !strings.HasSuffix(p, "/") { p = p + "/" } r := NewRoute("", p, nmux) mux.routes = append(mux.routes, r) sort.Sort(mux.routes) return r } func NewRestMux(path string) *RestMux { ret := &RestMux{ Path: path, routes: Routes{}, middlewares: NewMiddlewareLink(), } return ret }