// GetMethodHandler returns the appropriate method handler for the request or a // Method Not Allowed handler func (wc *WebController) GetMethodHandler(m int) func(w http.ResponseWriter, req *http.Request) { if m == Options { return func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Allow", wc.GetAllowedMethods()) w.Header().Set("Content-Length", "0") w.WriteHeader(http.StatusOK) } } if m == Head { return func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Allow", wc.GetAllowedMethods()) w.Header().Set("Content-Length", "0") w.WriteHeader(http.StatusOK) } } if h, ok := wc.handlers[m]; ok { return h } return func(w http.ResponseWriter, req *http.Request) { allowed := wc.GetAllowedMethods() w.Header().Set("Allow", allowed) render.Error( w, http.StatusMethodNotAllowed, fmt.Errorf("405 Method Not Allowed. Allowed: %s", allowed), ) } }
// BuildRouter collects all of the controllers, wires up the routes and returns // the resulting router func (ws *WebService) BuildRouter() *mux.Router { // Router // // StrictSlash forces the routes to be applied literally... // i.e. Route /foo/ with requests /foo will redirect to /foo/ // and route /bar with requests to /bar/ will redirect to /bar r := mux.NewRouter().StrictSlash(true) // Controllers rootSeen := false versionSeen := false links := EndPoints{} for _, wc := range ws.controllers { if !rootSeen && wc.Route == root { rootSeen = true } if !versionSeen && wc.Route == VersionRoute { versionSeen = true } // Add the handler for a route, and rate-limit it using throttle r.Handle( wc.Route, http.HandlerFunc(GetHandler(wc)), ) links = append(links, EndPoint{URL: wc.Route, Methods: wc.GetAllowedMethods()}) } // Profiling handlers // XXX: should we add them using the public api too? r.HandleFunc("/profiler/info.html", profiler.MemStatsHTMLHandler) links = append(links, EndPoint{URL: "/profiler/info.html", Methods: "GET"}) r.HandleFunc("/profiler/info", profiler.ProfilingInfoJSONHandler) r.HandleFunc("/profiler/start", profiler.StartProfilingHandler) r.HandleFunc("/profiler/stop", profiler.StopProfilingHandler) r.HandleFunc("/debug/pprof/", http.HandlerFunc(gopprof.Index)) links = append(links, EndPoint{URL: "/debug/pprof", Methods: "GET"}) r.HandleFunc("/debug/pprof/cmdline", http.HandlerFunc(gopprof.Cmdline)) r.HandleFunc("/debug/pprof/profile", http.HandlerFunc(gopprof.Profile)) r.HandleFunc("/debug/pprof/symbol", http.HandlerFunc(gopprof.Symbol)) if !versionSeen { // If detailed version info is not provided, we echo the default // This allows services to provide their own extended version info, i.e. // database versioning as well as process versioning r.HandleFunc(VersionRoute, func(w http.ResponseWriter, r *http.Request) { v := Version{} v.Hydrate() render.JSON(w, http.StatusOK, v) }) links = append(links, EndPoint{URL: VersionRoute, Methods: "GET"}) } // The last routes are the NotFound routes as we want to return JSON. // // This handles / on it's own, and we should only do this if no other // route already registered / if !rootSeen { sort.Sort(links) r.HandleFunc(root, func(w http.ResponseWriter, r *http.Request) { render.JSON(w, http.StatusOK, links) }) } // This is a wildcard route and will greedily consume all remaining routes r.HandleFunc("/{path:.*}", func(w http.ResponseWriter, r *http.Request) { render.Error( w, http.StatusNotFound, fmt.Errorf("/%s not found", mux.Vars(r)["path"]), ) }) return r }