func (r *AppGroup) HandlerFunc(method, uri string, handler http.HandlerFunc) {
	r.Handler(method, uri, handler)
}
HandlerFunc registers a new resource with http.HandlerFunc
package gogo
import (
	"context"
	"math"
	"net/http"
	"net/http/httputil"
	"path"
	"strings"
	"sync"
	"github.com/dolab/gogo/pkgs/gid"
	"github.com/dolab/httpdispatch"
)
type AppGroup struct {
	mux sync.RWMutex
	server  *AppServer
	prefix  string
	filters []Middleware
	handler Handler
}
func NewAppGroup(prefix string, server *AppServer) *AppGroup {
	dispatcher := httpdispatch.New()
	dispatcher.RedirectTrailingSlash = false
	dispatcher.HandleMethodNotAllowed = false  
	dispatcher.NotFound = NewNotFoundHandle(server)
	dispatcher.MethodNotAllowed = NewMethodNotAllowedHandle(server)
	return &AppGroup{
		server:  server,
		prefix:  prefix,
		handler: dispatcher,
	}
}
func (r *AppGroup) NewGroup(prefix string, filters ...Middleware) Grouper {
	r.mux.Lock()
	defer r.mux.Unlock()
	return &AppGroup{
		server:  r.server,
		handler: r.handler,
		prefix:  r.buildPrefix(prefix),
		filters: r.buildMiddlewares(filters...),
	}
}
func (r *AppGroup) SetHandler(handler Handler) {
	r.mux.Lock()
	r.handler = handler
	r.mux.Unlock()
}
func (r *AppGroup) Use(filters ...Middleware) {
	r.mux.Lock()
	defer r.mux.Unlock()
	r.filters = append(r.filters, filters...)
	if len(r.filters) >= math.MaxInt8 {
		panic(ErrTooManyMiddlewares)
	}
}
func (r *AppGroup) Middlewares() []Middleware {
	return r.filters
}
func (r *AppGroup) CleanMiddlewares() {
	r.mux.Lock()
	r.filters = []Middleware{}
	r.mux.Unlock()
}
func (r *AppGroup) OPTIONS(rpath string, handler Middleware) {
	r.Handle("OPTIONS", rpath, handler)
}
func (r *AppGroup) HEAD(rpath string, handler Middleware) {
	r.Handle("HEAD", rpath, handler)
}
func (r *AppGroup) POST(rpath string, handler Middleware) {
	r.Handle("POST", rpath, handler)
}
func (r *AppGroup) GET(rpath string, handler Middleware) {
	r.Handle("GET", rpath, handler)
}
func (r *AppGroup) PUT(rpath string, handler Middleware) {
	r.Handle("PUT", rpath, handler)
}
func (r *AppGroup) PATCH(rpath string, handler Middleware) {
	r.Handle("PATCH", rpath, handler)
}
func (r *AppGroup) DELETE(rpath string, handler Middleware) {
	r.Handle("DELETE", rpath, handler)
}
func (r *AppGroup) Any(rpath string, handler Middleware) {
	r.Handle("GET", rpath, handler)
	r.Handle("POST", rpath, handler)
	r.Handle("PUT", rpath, handler)
	r.Handle("PATCH", rpath, handler)
	r.Handle("DELETE", rpath, handler)
	r.Handle("HEAD", rpath, handler)
	r.Handle("OPTIONS", rpath, handler)
}
func (r *AppGroup) Static(rpath, root string) {
	if rpath[len(rpath)-1] != '/' {
		rpath += "/"
	}
	rpath += "*filepath"
	r.handler.ServeFiles(rpath, http.Dir(root))
}
func (r *AppGroup) Proxy(method string, rpath string, proxy *httputil.ReverseProxy) {
	handler := func(ctx *Context) {
		proxy.ServeHTTP(ctx.Response, ctx.Request)
	}
	switch method {
	case "*":
		r.Any(rpath, handler)
	default:
		r.Handle(method, rpath, handler)
	}
}
func (r *AppGroup) Resource(resource string, controller interface{}) Grouper {
	resource = strings.TrimSuffix(resource, "/")
	if resource[0] != '/' {
		resource = "/" + resource
	}
	var (
		idSuffix     string
		resourceSpec string
	)
	id, ok := controller.(ControllerID)
	if ok {
		idSuffix = strings.TrimSpace(id.ID())
	}
	if idSuffix == "" {
		names := strings.Split(strings.Trim(resource, "/"), "/")
		idSuffix = strings.ToLower(names[len(names)-1])
	}
	resourceSpec = resource + "/:" + idSuffix
	dispatch, ok := controller.(ControllerDispatch)
	if ok {
		r.Any(resource, dispatch.DISPATCH)
		r.Any(resourceSpec, dispatch.DISPATCH)
		return r.NewGroup(resourceSpec)
	}
	index, ok := controller.(ControllerIndex)
	if ok {
		r.GET(resource, index.Index)
	}
	create, ok := controller.(ControllerCreate)
	if ok {
		r.POST(resource, create.Create)
	}
	head, ok := controller.(ControllerExplore)
	if ok {
		r.HEAD(resourceSpec, head.Explore)
	}
	show, ok := controller.(ControllerShow)
	if ok {
		r.GET(resourceSpec, show.Show)
	}
	update, ok := controller.(ControllerUpdate)
	if ok {
		r.PUT(resourceSpec, update.Update)
	}
	delete, ok := controller.(ControllerDestroy)
	if ok {
		r.DELETE(resourceSpec, delete.Destroy)
	}
	return r.NewGroup(resourceSpec)
}