Outdated documentation
You are reading documentation for v3, which is an outdated version. Click here to go to the latest documentation.
# Routing
# Introduction
Routing is an essential part of any Goyave application. Routes definition is the action of associating a URI, sometimes having parameters, with a handler which will process the request and respond to it. Separating and naming routes clearly is important to make your API or website clear and expressive.
All features below require the goyave
package to be imported.
import "goyave.dev/goyave/v3"
Routes are defined in routes registrer functions. The main route registrer is passed to goyave.Start()
and is executed automatically with a newly created root-level router.
func Register(router *goyave.Router) {
// Register your routes here
}
# Basic routing
Although it's not recommended, routes can be defined using closures. This is a very simple way of defining routes that can be used for scaffolding or quick testing.
router.Route("GET", "/hello", func(response *goyave.Response, r *goyave.Request) {
response.String(http.StatusOK, "Hi!")
})
# Router.Route
Register a new route.
Multiple methods can be passed using a pipe-separated string.
If the route matches the GET
method, the HEAD
method is automatically added to the matcher if it's missing.
If the router has CORS options set, the OPTIONS
method is automatically added to the matcher if it's missing, so it allows pre-flight requests.
Returns the generated route.
Parameters | Return |
---|---|
methods string | *goyave.Route |
uri string | |
handler goyave.Handler |
Examples:
router.Route("GET", "/hello", myHandlerFunction)
router.Route("POST", "/user", user.Register)
router.Route("PUT|PATCH", "/user", user.Update)
router.Route("POST", "/product", product.Store)
TIP
goyave.Handler
is an alias for func(*goyave.Response, *goyave.Request)
.
You can also register routes by using the Get
, Post
, Put
, Patch
, Delete
and Options
methods:
router.Get("/hello", myHandlerFunction)
router.Post("/user", user.Register)
router.Put("/product/{id:[0-9]+}", product.Update)
router.Patch("/product/{id:[0-9]+}", product.Update)
router.Delete("/product/{id:[0-9]+}", product.Destroy)
router.Options("/options", myHandlerFunction)
Parameters | Return |
---|---|
uri string | *goyave.Route |
handler goyave.Handler |
# Route reference
Since v2.6.0
Name GetName BuildURL BuildURI GetURI GetFullURI GetMethods GetHandler GetValidationRules Validate Middleware
# Route.Name
Set the name of this route.
Panics if a route with the same name already exists.
Returns itself.
Parameters | Return |
---|---|
name string | *goyave.Route |
Example:
router.Route("GET", "/product/{id:[0-9]+}", myHandlerFunction).Name("product.show")
# Route.GetName
Get the name of this route.
Parameters | Return |
---|---|
string |
Example:
fmt.Println(route.GetName()) // "product.create"
# Route.BuildURL
Build a full URL pointing to this route.
Panics if the amount of parameters doesn't match the amount of actual parameters for this route.
Parameters | Return |
---|---|
parameters ...string | string |
Example:
fmt.Println(route.BuildURL("42")) // "http://localhost:8080/product/42"
# Route.BuildURI
Since v3.8.0
Build a full URI pointing to this route. The returned string doesn't include the protocol and domain. (e.g. "/user/login")
Panics if the amount of parameters doesn't match the amount of actual parameters for this route.
Parameters | Return |
---|---|
parameters ...string | string |
Example:
fmt.Println(route.BuildURI("42")) // "/product/42"
# Route.GetURI
Get the URI of this route.
The returned URI is relative to the parent router of this route, it is NOT the full path to this route.
Note that this URI may contain route parameters in their définition format. Use the request's URI if you want to see the URI as it was requested by the client.
Parameters | Return |
---|---|
string |
Example:
fmt.Println(route.GetURI()) // "/{id:[0-9]+}"
# Route.GetFullURI
Get the full URI of this route.
Note that this URI may contain route parameters in their définition format. Use the request's URI if you want to see the URI as it was requested by the client.
Parameters | Return |
---|---|
string |
Example:
fmt.Println(route.GetFullURI()) // "/product/{id:[0-9]+}"
# Route.GetMethods
Returns the methods the route matches against.
Parameters | Return |
---|---|
[]string |
Example:
fmt.Println(route.GetMethods()) // [GET OPTIONS]
# Route.GetHandler
Since v3.8.0
Returns the Handler associated with this route.
Parameters | Return |
---|---|
goyave.Handler |
# Route.GetValidationRules
Since v3.8.0
Returns the validation rules associated with this route.
Parameters | Return |
---|---|
*validation.Rules |
# Route.GetFullURIAndParameters
Since v3.8.0
Get the full uri and parameters for this route and all its parent routers.
Parameters | Return |
---|---|
uri string | |
parameters []string |
Example:
subrouter := router.Subrouter("/{article}")
route := subrouter.Get("/{id:[0-9+]}")
fmt.Println(route.GetFullURIAndParameters()) // "/{article}/{id:[0-9+]}" [article id]
# Route.Validate
Since v3.0.0
Validate adds validation rules to this route. If the user-submitted data doesn't pass validation, the user will receive an error and messages explaining what is wrong.
Returns itself.
Parameters | Return |
---|---|
validationRules validation.Ruler | *goyave.Route |
Example:
router.Post("/user", user.Register).Validate(user.RegisterRequest)
TIP
Learn more about validation and rules sets here.
# Route.Middleware
Since v3.0.0
Register middleware for this route only.
Returns itself.
Parameters | Return |
---|---|
middleware ...goyave.Middleware | *goyave.Route |
Example:
router.Put("/product", product.Update).Middleware(middleware.Admin)
TIP
Learn more about middleware here.
# Route parameters
URIs can have parameters, defined using the format {name}
or {name:pattern}
. If a regular expression pattern is not defined, the matched variable will be anything until the next slash.
Example:
router.Get("/product/{key}", product.Show)
router.Get("/product/{id:[0-9]+}", product.ShowById)
router.Get("/category/{category}/{id:[0-9]+}", category.Show)
Regex groups can be used inside patterns, as long as they are non-capturing ((?:re)
). For example:
router.Get("/category/{category}/{sort:(?:asc|desc|new)}", category.ShowSorted)
Route parameters can be retrieved as a map[string]string
in handlers using the request's Params
attribute.
func myHandlerFunction(response *goyave.Response, request *goyave.Request) {
category := request.Params["category"]
id, _ := strconv.Atoi(request.Params["id"])
//...
}
# Handling HEAD
The HEAD
HTTP method requests the headers that would be returned if the request's URL was instead requested with the HTTP GET
method. The HEAD
method is automatically handled for routes matching the GET
method. When a route is matched with the HEAD
method, it is executed as usual but the response body is discarded. That means that database queries and other operations are executed.
You may want to add a route definition exclusively for the HEAD
method to prevent expensive operations to be executed. Register it before the corresponding GET
route so it will be matched first. Keep in mind the returned headers should be the same as the ones returned by the GET
handler!
router.Route("HEAD", "/test", func(response *Response, r *Request) {
response.Header().Set("Content-Type", "application/json; charset=utf-8")
response.Status(http.StatusOK)
})
router.Get("/test", func(response *Response, r *Request) {
response.JSON(http.StatusOK, map[string]string{"message": "hello world"})
})
# Named routes
Since v2.6.0
It is possible to give a name to your routes to make it easier to retrieve them later and build dynamic URLs.
router.Route("GET", "/product/{id:[0-9]+}", myHandlerFunction).Name("product.show")
The route can now be retrieved from any router or from the global helper:
route := router.GetRoute("product.show")
// or
route := goyave.GetRoute("product.show")
fmt.Println(route.BuildURL("42")) // "http://localhost:8080/product/42"
# goyave.GetRoute
Get a named route. Returns nil if the route doesn't exist.
Parameters | Return |
---|---|
name string | *goyave.Route |
# Validation
You can assign a validation rules set to each route. Learn more in the dedicated section. You should always validate incoming requests.
router.Route("POST", "/product", product.Store).Validate(validation.RuleSet{
"Name": {"required", "string", "min:4"},
"Price": {"required", "numeric"},
})
TIP
It's not recommended to define rules set directly in the route definition. You should define rules sets in your controller package.
# Middleware
Middleware are handlers executed before the controller handler. Learn more in the dedicated section.
Middleware are applied to a router or a sub-router before the routes definition. Therefore, all routes in that router and its sub-routers will execute them before executing their associated handler.
To assign a middleware to a router, use the router.Middleware()
function. Many middleware can be assigned at once. The assignment order is important as middleware will be executed in order.
# Router.Middleware
Middleware apply one or more middleware to the route group.
Parameters | Return |
---|---|
middleware ...goyave.Middleware | void |
Example:
router.Middleware(middleware.DisallowNonValidatedFields)
Middleware can also be applied to specific routes. You can add as many as you want.
Example:
router.Route("POST", "/product", product.Store).Validate(product.StoreRequest).Middleware(middleware.Trim)
# Groups and sub-routers
Grouping routes makes it easier to define multiple routes having the same prefix and/or middleware.
Let's take a simple scenario where we want to implement a user CRUD. All our routes will start with /user
, so we are going to create a sub-router for it:
userRouter := router.Subrouter("/user")
In our application, user profiles are public: anyone can see the user profiles without being authenticated. However, only authenticated users can modify their information and delete their account. We don't want to add some redundancy and apply the authentication middleware for each route needing it, so we are going to create another sub-router. Sub-routers having an empty prefix are called route groups and can be created using router.Group()
.
userRouter.Get("/{username}", user.Show)
userRouter.Post("", user.Register).Validate(user.RegisterRequest)
authUserRouter := userRouter.Group()
authUserRouter.Middleware(authenticationMiddleware)
authUserRouter.Put("/{id}", user.Update).Validate(user.UpdateRequest)
authUserRouter.Delete("/{id}", user.Delete)
To improve your routes definition readability, you should create a new route registrer for each feature. In our example, our definitions would look like this:
func registerUserRoutes(router *goyave.Router) {
//...
}
// Register is the main route registrer.
func Register(router *goyave.Router) {
registerUserRoutes(router)
registerProductRoutes(router)
//...
}
Sub-routers are checked before routes, meaning that they have priority over the latter. If you have a router sharing a prefix with a higher-level level route, it will never match because the sub-router will match first.
subrouter := router.Subrouter("/product")
subrouter.Get("/{id:[0-9]+}", handler)
router.Get("/product/{id:[0-9]+}", handler) // This route will never match
router.Get("/product/category", handler) // This one neither
# Serve static resources
The Goyave router provides a way to serve a directory of static resources, including its sub-directories.
Let's say you have the following directory structure:
.
└── static
├── js
│ └── index.js
├── img
│ ├── favicon.ico
│ └── logo.png
├── css
│ └── styles.css
└── index.html
If you want to serve the static
directory, register the following route:
router.Static("/", "static", false)
If a user requests http://yourdomain.com/js/index.js
, the corresponding file will be sent as a response.
If no file is given (http://yourdomain.com/
), or if the request URI is a directory (http://yourdomain.com/img
), Goyave will look for a index.html
file and send it if it exists. An error 404 Not Found is otherwise returned.
TIP
This method is especially useful to serve Single Page Applications from your API. (Angular, Vue.js, React applications)
# Router.Static
Static serve a directory and its sub-directories of static resources.
Set the download
parameter to true if you want the files to be sent as an attachment instead of an inline element.
The directory
parameter can be a relative or an absolute path.
Parameters | Return |
---|---|
uri string | void |
directory string | |
download bool | |
middleware ...goyave.Middleware |
Example:
router.Static("/public", "/path/to/static/dir", false)
# Native handlers
Since v2.0.0
# goyave.NativeHandler
NativeHandler is an adapter function for http.Handler
. With this adapter, you can plug non-Goyave handlers to your application.
Just remember that the body contains the raw data, which haven't been validated nor converted. This means that native handlers are not guaranteed to work and cannot modify the request data. Request properties, such as headers, can still be modified.
The actual response writer passed to the native handler is a goyave.Response
.
WARNING
This feature is a compatibility layer with the rest of the Golang web ecosystem. Prefer using Goyave handlers if possible.
Parameters | Return |
---|---|
handler http.Handler | goyave.Handler |
Example:
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world"))
})
router.Route("GET", "/user", goyave.NativeHandler(httpHandler))
# goyave.NativeMiddleware
NativeMiddleware
is an adapter function for standard library middleware.
Native middleware work like native handlers. See NativeHandler
for more details.
Parameters | Return |
---|---|
middleware goyave.NativeMiddlewareFunc | goyave.Middelware |
TIP
goyave.NativeMiddlewareFunc
is defined as follows:
// NativeMiddlewareFunc is a function which receives an http.Handler and returns another http.Handler.
type NativeMiddlewareFunc func(http.Handler) http.Handler
Example:
middleware := goyave.NativeMiddleware(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world"))
next.ServeHTTP(w, r) // Don't call "next" if your middleware is blocking.
})
})
router.Middleware(middleware)