// NewWebService provides a way to create a new blank WebService func NewWebService() WebService { ws := WebService{} // add standard routes // Stats controller ws.stats = stats.New() statsController := NewWebController("/stats") statsController.AddMethodHandler(Get, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusOK) b, _ := json.Marshal(ws.stats.Data()) w.Write(b) }) ws.AddWebController(statsController) // Heartbeat controller (echoes the default version info) heartbeatController := NewWebController("/heartbeat") heartbeatController.AddMethodHandler(Get, func(w http.ResponseWriter, r *http.Request) { v := Version{} v.Hydrate() render.JSON(w, http.StatusOK, v) }) ws.AddWebController(heartbeatController) return ws }
// Handles POST for /verify func (validator accountValidator) AccountValidatorPost(w http.ResponseWriter, req *http.Request) { var transformedRequest bankAccountRequest // Decode the JSON payload if err := json.NewDecoder(req.Body).Decode(&transformedRequest); err != nil { responses.WriteUnprocessableEntity(w) return } // Check that we got an expected format if !transformedRequest.isValid() { message := "Expected a 6 digits sort code and an account number between 6 and 10 digits." responses.WriteMessage(http.StatusBadRequest, "invalid_bank_account", message, w) return } // Create the required bank account b := models.CreateBankAccount(transformedRequest.SortCode, transformedRequest.AccountNumber) // Check if the bank account is valid isValid := validator.resolver.IsValid(b) // Construct and render the final response response := ValidityResponse{ Valid: isValid, SortCode: b.SortCode, AccountNumber: b.AccountNumber, } render.JSON(w, http.StatusOK, response) }
// Write a message with a given status code, a status and a message func WriteMessage(code int, status string, message string, w http.ResponseWriter) { response := APIMessage{ code: code, Status: status, Message: message, } render.JSON(w, response.code, response) }
// NewWebService provides a way to create a new blank WebService func NewWebService() WebService { ws := WebService{} // Heartbeat controller (echoes the default version info) heartbeatController := NewWebController(HeartbeatRoute) heartbeatController.AddMethodHandler(Get, func(w http.ResponseWriter, r *http.Request) { v := Version{} v.Hydrate() render.JSON(w, http.StatusOK, v) }, ) ws.AddWebController(heartbeatController) return ws }
// 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 }