I've started a small personal API as a learning exercise with Go and while trying to test it I found that the endpoint GET /find/{id} isn't triggered when doing such call in Postman.
Mux Router:
router.HandleFunc("/find/{id}", controller.Find).Methods("GET")
Controller method:
func Find(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
...
}
And the former call to the API:
localhost:8080/find/[cb&%AD%87"%8CV
Maybe it's something really simple that I can't really see?
I'm using Gorilla mux,
The implementation is similar, and you can read more about it in here
package HTTPServer
import (
"net/http"
"github.com/gorilla/mux"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Routes []Route
var routes = Routes{
{"GetFirstForm", "GET", "/firstForm/{id}", GetFirstForm}{
//GetFirstForm getting first form by ECO id
func GetFirstForm(w http.ResponseWriter, r *http.Request) {
var vars = mux.Vars(r)
ecoID := vars["id"]
///your logic
}
Related
So, I'm working on a simple RESTful API using Go and Gorilla Mux. I'm having issues with with my second route not working, it's returning a 404 error. I'm not sure what the problem is as I'm new to Go and Gorilla. I'm sure it's something really simple, but I can't seem to find it. I think it might be a problem with the fact that I'm using different custom packages.
This question is similar, Routes returning 404 for mux gorilla, but the accepted solution didn't fix my problem
Here's what my code looks like:
Router.go:
package router
import (
"github.com/gorilla/mux"
"net/http"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Routes []Route
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(route.HandlerFunc)
}
return router
}
var routes = Routes{
Route{
"CandidateList",
"GET",
"/candidate",
CandidateList,
},
Route{
"Index",
"GET",
"/",
Index,
},
}
Handlers.go
package router
import (
"fmt"
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Welcome!")
}
func CandidateList(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "CandidateList!")
}
Main.go
package main
import (
"./router"
"log"
"net/http"
)
func main() {
rout := router.NewRouter()
log.Fatal(http.ListenAndServe(":8080", rout))
}
Going to just localhost:8080 returns Welcome! but going to localhost:8080/candidate returns a 404 Page Not Found error. I appreciate any input and help! Thanks!
This is an updated version of my Router.go file, there is still the same issue happening.
Router.go
package router
import (
"github.com/gorilla/mux"
"net/http"
)
type Route struct {
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Routes []Route
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
router.
Methods(route.Method).
Path(route.Pattern).
Handler(route.HandlerFunc).GetError()
}
return router
}
var routes = Routes{
Route{
"GET",
"/candidate",
CandidateList,
},
Route{
"GET",
"/",
Index,
},
}
It appears that my project was holding onto old versions of the Router.go and Handlers.go files in the main src directory. By removing these duplicate files and re-running Main.go with go run Main.go I was able to get the route to be recognized.
i am using Gorilla Mux for writing a REST API and i am having trouble organizing my routes, currently all of my routes are defined in the main.go file like this
//main.go
package main
import (
"NovAPI/routes"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/hello", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello")
})
router.HandleFunc("/user", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "User")
})
router.HandleFunc("/route2", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route2")
})
router.HandleFunc("/route3", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route3")
})
// route declarations continue like this
http.ListenAndServe(":1128", router)
}
so what i want to do is take out and split this route declaration into multiple files, how would i go about doing that? thanks in advance.
You can modularize your routers independently into different packages, and mount them on the main router
Elaborating just a little on the following issue, you can come up with this approach, that makes it quite scalable (and easier to test, to some degree)
/api/router.go
package api
import (
"net/http"
"github.com/gorilla/mux"
)
func Router() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/", home)
return router
}
func home(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello from API"))
}
/main.go
package main
import (
"log"
"net/http"
"strings"
"github.com/...yourPath.../api"
"github.com/...yourPath.../user"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/", home)
mount(router, "/api", api.Router())
mount(router, "/user", user.Router())
log.Fatal(http.ListenAndServe(":8080", router))
}
func mount(r *mux.Router, path string, handler http.Handler) {
r.PathPrefix(path).Handler(
http.StripPrefix(
strings.TrimSuffix(path, "/"),
handler,
),
)
}
func home(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Home"))
}
What about something like this ?
//main.go
package main
import (
"NovAPI/routes"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/hello", HelloHandler)
router.HandleFunc("/user", UserHandler)
router.HandleFunc("/route2", Route2Handler)
router.HandleFunc("/route3", Route3Handler)
// route declarations continue like this
http.ListenAndServe(":1128", router)
}
func HelloHandler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello")
}
func UserHandler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "User")
}
func Route2Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route2")
}
func Route3Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route3")
}
That way you can put your handlers in other files, or even other packages.
If you endup with additionnal dependencies like a database, you can even avoid the need of the global var using a constructor trick:
//main.go
func main() {
db := sql.Open(…)
//...
router.HandleFunc("/hello", NewHelloHandler(db))
//...
}
func NewHelloHandler(db *sql.DB) func(http.ResponseWriter, *http.Request) {
return func(res http.ResponseWriter, req *http.Request) {
// db is in the local scope, and you can even inject it to test your
// handler
fmt.Fprintln(res, "Hello")
}
}
I like checking out other projects in github to grab ideas on how to do stuff, and for these cases I usually take a look first at the Docker repo. This is the way they do it:
For the system's routes, define all handlers in system_routes.go and then initialize those routes on a NewRouter function in system.go.
type systemRouter struct {
backend Backend
routes []router.Route
}
func NewRouter(b Backend) router.Router {
r := &systemRouter{
backend: b,
}
r.routes = []router.Route{
local.NewOptionsRoute("/", optionsHandler),
local.NewGetRoute("/_ping", pingHandler),
local.NewGetRoute("/events", r.getEvents),
local.NewGetRoute("/info", r.getInfo),
local.NewGetRoute("/version", r.getVersion),
local.NewPostRoute("/auth", r.postAuth),
}
return r
}
// Routes return all the API routes dedicated to the docker system.
func (s *systemRouter) Routes() []router.Route {
return s.routes
}
Notice that systemRouter implements the router.Router interface and the Routes function returns a []router.Route, and their handlers are defined as
func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error
instead of Go's standard http handler:
func(w http.ResponseWriter, r *http.Request)
So there's extra code of theirs to convert a Docker API Handler to a Go HTTP Handler at the makeHttpHandler function.
And finally, to add those routes to their mux router, on their server.go they implement several other functions to add middleware to their handlers.
If this is something that you think it's what you are looking for, then take your time to analyze the Docker code for their routes, and if you need me to elaborate more or if I missed anything, post a comment.
Since I am new to Go, I prefer a less verbose solution. In each module, we can create a Route function that expects a main route pointer and creates sub-routes to it. Our main.go file would be as follows
package main
import (
"net/http"
"github.com/user-name/repo-name/auth"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
auth.Router(r)
http.ListenAndServe(":8080", r)
}
then in auth module, we can create a route file
package auth
import "github.com/gorilla/mux"
func Router(r *mux.Router) {
routes := r.PathPrefix("/auth").Subrouter()
routes.HandleFunc("/register", Register)
}
I have a Context struct that is used for the Application Context. The ConfigureRouter function receives the context as a parameter and sets the global variable c so middleware in the same file can use it.
var c *core.Context
func ConfigureRouter(ctx *core.Context, router *httprouter.Router) {
c = ctx //make context available in all middleware
router.POST("/v1/tokens/create", token.Create) //using httprouter
}
The one route listed above calls token.Create ( from the token package which is a sub-directory) but it needs the context too.
//token/token.go
func Create(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
//Help! I need the context to do things
}
How can I pass the context to the token package?
You can redefine your Create function as a function that returns the handler function:
func Create(ctx *core.Context) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
// Now you have the context to do things
}
}
Where httprouter.Handle is a func type defined by httprouter to be type Handle func(http.ResponseWriter, *http.Request, Params).
And then you would use it like this in ConfigureRouter:
func ConfigureRouter(ctx *core.Context, router *httprouter.Router) {
router.POST("/v1/tokens/create", token.Create(ctx))
}
I am starting building a api in go(golang), but I have few questions...
So in my main function or init function(because I might use appengine) I was thinking in calling a function which will define all my routes using gorilla mux. Each pice of my application(post, comments etc...) will have its one package with its structures/methods/functions.
Questions:
Because I was thinking in defining the routes in one function, do I need to import in this file all my packages, to send the requests to the right handlers?
What about helper function, for example I would like to set content type of the response to be application/json for all the handlers where this is necessary, how I will be able to do that?
I'm not looking for frameworks, just some pointer about how can I overcome those questions in golang way.
If you define all of the routes in a single function, then the file containing this function will need to import the packages that implement the handlers. The only way to refer to a type or function in another package is to import the package.
Here's a helper for setting the content type and encoding a value to JSON:
func JSONHandler(f func(w http.ResponseWriter, r *http.Request) interface{}) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
v := f(w, r)
if v != nil {
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(v); err != nil {
log.Println(err)
}
}
})
}
The argument to this function is a function that returns a value to encode to the response as JSON. For example, this function returns the client's user agent as JSON.
func UserAgentHandler(w http.ResponseWriter, r *http.Request) interface{} {
return struct { UserAgent string }{ req.Header.Get("User-Agent") }
}
Use the following code to register this handler with the Gorilla mux r:
r.Handle("/user-agent", JSONHandler(UserAgentHandler))
There are many ways to improve JSONHandler.
Using code below, when I access /test2 it responds with 404 - not found. /test1 works correctly. Why is that? Is nesting not allowed despite the fact that routers implement http.Handler interface?
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mainRouter := mux.NewRouter()
subRouter := mux.NewRouter()
mainRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test1") })
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test2") })
mainRouter.Handle("/", subRouter)
http.ListenAndServe(":9999", mainRouter)
}
EDIT:
My main goal was to add some initial work which would be common for all routes in subRouter, and only for them. To be even more specific, I would like to use Negroni as my middleware orchiestrator.
On the Negroni website there is an example of adding middleware to the group of routes:
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// add admin routes here
Create a new negroni for the admin middleware
router.Handle("/admin", negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
Negroni basically executes ServeHTTP methods of every argument, since all of them implement http.Handler. It executes them in order, so router routes will be last.
I'm familiar with the concept of Subrouter in Mux, but AFAIK I can't use it in similar fashion as example above, in particular, I can't inject anything between mainRouter and its Subrouter. This is why nesting looks more flexible.
I know this question is somewhat old, but I have spent some time figuring out how handlers and matching work in go. You can see my experiment code here.
Basically, you can get the effect you want with code like this:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mainRouter := mux.NewRouter()
subRouter := mux.NewRouter()
mainRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "test1")
})
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "test2")
})
// in mux, you need to register subrouter
// with the same path that the handlers in
// it are matching
mainRouter.Handle("/test2", subRouter)
// if your subrouter has handlers that match
// other sub paths - you also need to do this
mainRouter.Handle("/test2/{_dummy:.*}", subRouter)
http.ListenAndServe(":9999", mainRouter)
}
I hope this helps someone.
None of the previous answers given helped me achieve exactly what I was seeking out to do. I was trying to use negroni.Wrap() around a Subrouter with lots of routes. I believe that is what the original poster wanted.
Additional Context: I am deploying on Google App Engine, hence putting everything in the init() function.
package hello
import (
"fmt"
"net/http"
"github.com/codegangsta/negroni"
"github.com/gorilla/mux"
)
func init() {
// Create the "root" router, if you will...
r := mux.NewRouter().StrictSlash(true)
// Create your "Subrouter" dedicated to /api which will use the PathPrefix
apiRouter := mux.NewRouter().PathPrefix("/api").Subrouter().StrictSlash(true)
// This step is where we connect our "root" router and our "Subrouter" together.
r.PathPrefix("/api").Handler(negroni.New(
negroni.HandlerFunc(myMiddleware),
negroni.Wrap(apiRouter),
))
// Define "root" routes using r
r.HandleFunc(genHandleFunc("/", "root of site"))
r.HandleFunc(genHandleFunc("/home", "home"))
// Define "Subrouter" routes using apiRouter, prefix is /api
apiRouter.HandleFunc(genHandleFunc("/", "root of API, /api")) // Matches: /api
apiRouter.HandleFunc(genHandleFunc("/v1", "root of API V1, /api/v1")) // Matches: /api/v1
apiRouter.HandleFunc(genHandleFunc("/v1/resourceabc", "API V1 - resourceabc, /api/v1/resourceabc")) // Matches: /api/v1/resourceabc
/* Finally we pass our "root" router to the net/http library. The "root" router will contain all
of the routes for /api also.
*/
http.Handle("/", r)
}
// Silly function to quickly generate a HandleFunc
func genHandleFunc(p string, msg string) (path string, f func(http.ResponseWriter, *http.Request)) {
path = p
f = func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v\n", msg)
}
return
}
func myMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Fprintln(w, "Before doing work...")
next(w, r)
fmt.Fprintln(w, "After doing work...")
}
You wouldn't use two routers here anyway.
Gorilla Mux has the concept of a Subrouter, whereby you define the top level domain properties on the main router, then use the Subrouter instance to map the individual paths.
For example:
mainRouter := mux.NewRouter()
subRouter := mainRouter.PathPrefix("/").Subrouter()
subRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test1") })
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test2") })
mainRouter.Handle("/", mainRouter)
You can go even further than that though - for example, you may have another router at /test1 and a subrouter that matches anything further below that (say /test1/othertest).
Use full path in subroutes:
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
apiRoutes.Handle("/api/auth", Auth)
router.PathPrefix("/api").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(apiRoutes),
))