gohttp/server/server.go

175 lines
3.9 KiB
Go

package server
import (
"context"
"net/http"
"sort"
"strings"
)
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 {
if route.Method != "" && route.Method != r.Method {
return false
}
if route.matcher != 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
}
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)
}
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
}