Routing

Basic Routing

The most basic Confetti routes accept a URI and a Closure, providing a very simple and expressive method of defining routes:

Get("/user", func(request inter.Request) inter.Response {
    return outcome.Html("Hello World")
})

If you are using multiple routers, it is recommended to place a reference to a function in the second parameter:

Get("/user", controllers.User),

The Default Route Files

All Confetti routes are defined in your route files, which are located in the routes directory. These files are automatically loaded by the framework. The routes/web.go file defines routes that are for your web interface. These routes are assigned the Web middleware group. The routes in routes/api.go are stateless and are assigned the Api middleware group.

The routes defined in routes/web.go may be accessed by entering the defined route's URL in your browser. For example, you may access the following route by navigating to http://localhost/user in your browser:

Get("/user", controllers.User),

Routes defined in the routes/api.go file are nested within a route group. Within this group, the /api URI prefix is automatically applied, so you do not need to manually apply it to every route in the file. You may modify the prefix and other route group options by using multiple methods.

Available Router Methods

The router allows you to register routes that respond to any HTTP verb:

Get("/photos", controllers.PhotosIndex),
Post("/photos", controllers.PhotosStore),
Put("/photos/{photo}", controllers.PhotosUpdate),
Patch("/photos/{photo}", controllers.PhotosUpdate),
Delete("/photos/{photo}", controllers.PhotosDestroy),

Sometimes you may need to register a route that responds to multiple HTTP verbs. You may do so using the Match method:

Match([]string{method.Get, method.Post}, "/photos/{photo}", controllers.Photos)

Or, you may even register a route that responds to all HTTP verbs using the Any method:

Any("/photos/{photo}", controllers.Photos,

Redirect Routes

If you are defining a route that redirects to another URI, you may use Redirect. This function provides a convenient shortcut so that you do not have to define a full route or controller for performing a simple redirect:

Redirect("/here", "/there", 302)

You may use the RedirectTemporary method to return a 302 status code:

RedirectTemporary("/here", "/there")

Or the RedirectPermanent method to return a 301 status code:

RedirectPermanent("/here", "/there")

View Routes

If your route only needs to return a view, you may use the View method. Like the Redirect method, this method provides a simple shortcut so that you do not have to define a full route or controller. The View method accepts a URI as its first argument, and a view name as its second argument:

View('/profile', views.Profile("James"));

Route Parameters

Required Parameters

Sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a username from the URL. You may do so by defining route parameters:

Get("/user/{username}", func(request inter.Request) inter.Response {
    name := request.Parameter("username").String()
    return outcome.Html("User " + name)
}),

You may define as many route parameters as required by your route:

Get("posts/{post_id}/comments/{comment_alias}", func(request inter.Request) inter.Response {
    postId := request.Parameter("post_id").Int()
    commentAlias := request.Parameter("comment_alias").String()
    //
}),

If the parameter cannot be found, then it will panic with an error. Do you want more control over the errors? Then use methods with the suffix E to receive the error:

postId, err := request.ParameterE("post_id")
if err != nil {
    // postId.Int()
}

Route parameters are always encased within {} braces and should consist of alphabetic characters, and may not contain a - character. Instead of using the - character, use an underscore (_).

Optional Parameters

Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a ? mark after the parameter name. If no value is found, an empty string or a zero is given.

Get("users/{username?}/comment/{comment_id?}", func(request inter.Request) inter.Response {
    userName := request.Parameter("username").String()
    // userName = ""

    commentId := request.Parameter("comment_id").Int()
    // commentId = 0
}),
 






Use Empty or Present to check if a value is present.

userName := request.Parameter("username")
if userName.Empty() {
    // userName is empty
}

commentId := request.Parameter("comment_id")
if commentId.Filled() {
    // commentId.Int()
}

 




 


Regular Expression Constraints

You may constrain the format of your route parameters using regex. The key and regular expression must be separated by a colon:

Get("/user/{username:[a-z_]+}", controllers.UserShow)
Get("/user/{id:[0-9]+}", controllers.UserShow)

Or define a restriction separately:

Get("/user/{id}", controllers.UserShow).Where("id", "[0-9]+")

Global Constraints

If you would like a route parameter to always be constrained by a given regular expression, you may use the WhereMulti method. You should define these patterns in the Boot method of your RouteServiceProvider after the collection is filled:

func (p RouteServiceProvider) Boot(container inter.Container) inter.Container {
    collection := routing.NewRouteCollection()

    collection.Merge(routes.Api)
    collection.Merge(routes.Web)

    collection.WhereMulti(map[string]string{
        "id": "[0-9]+",
        "username": "[a-z_]+",
    })

    //
}






 
 
 
 



Once the pattern has been defined, it is automatically applied to all routes using that parameter name:

// Only executed if {id} is numeric...
Get("/user/{id}", controllers.UserShow),

Encoded Forward Slashes

The Confetti routing component allows all characters except /. You must explicitly allow / to be part of your placeholder using a Where condition regular expression:

Get("/search/{search}", controllers.UserIndex).Where("search", ".*")

Encoded forward slashes are only supported within the last route segment.

Route Model Binding

For simple transformation of a parameter to a model, you can bind a model to a parameter. You can use the RouteModelBinding middleware for this:

func (r RouteModelBinding) Handle(request inter.Request, next inter.Next) inter.Response {
	request.App().Singleton("user", func() model.User {
		userId := request.Parameter("user_id").Int()
		return model.FindUser(userId)
	})

    return next(request)
}

 
 
 
 



Later you can retrieve the user from the container:

user := request.Make("user").(model.User)

The callback ensures that the user is only received from the database when you need it (one time in each request). This can be nice if you first want to validate the request before accessing the database.

Named Routes

Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the Name method onto the route definition:

Get("/user/roles", controllers.UserRoleIndex).Name("user.roles")
Get("/user/comments", controllers.UserCommentIndex).Name("user.comments")

Or you can name a group:

Group(
    Get("/roles", controllers.UserRoleIndex).Name("roles"),
    Get("/comments", controllers.UserCommentIndex).Name("comments"),
).Prefix("/user").Name("user.")

The names of the above routes are user.roles and user.comments.

Generating URLs To Named Routes

Once you have assigned a name to a given route, you may use the route's name when redirects via the RedirectToRoute function:

// Generating Redirects...
Get("/comments", func(request inter.Request) inter.Response {
    return outcome.RedirectToRoute(request.App(), "user.roles")
})

Or you use the route's name when generating URLs via the UrlByName function:

// Generating URLs...
url := routing.UrlByName(app, "user.roles")

If the named route defines parameters, you may pass the parameters as the third argument to the UrlByName function. The given parameters will automatically be inserted into the URL in their correct positions:

Get("/user/{id}", controllers.UserShow).Name("user")

url := outcome.UrlByName(request.App(), "user", outcome.Parameters{"id": 12})

/user/12


 


If you also want to build a query string, use the 4th parameter, those key / value pairs will automatically be added to the generated URL's query string:

Get("/user/{id}", controllers.UserShow).Name("user")

routing.UrlByName(
    app,
    "user",
    routing.Parameters{"id": 12},
    routing.Parameters{"order_by": "name", "size": 50},
)

/user/12?order_by=name&size=50






 



Inspecting The Current Route

If you would like to determine if the current route name contains a given string. You may use the Named method on a Route instance. For example, you may check the current route name from a route middleware:

func (v ValidatePostSize) Handle(request inter.Request, next inter.Next) inter.Response {
    if request.Route().Named("profile") {
        //
    }

    return next(request)
}

 
 
 



Route Groups

Route groups allow you to share route attributes, such as middleware or prefixes, across many routes without needing to define those attributes on each individual route. Shared attributes are specified after the Group method.

Nested groups attempt to intelligently "merge" attributes with their parent group.

Middleware

To assign middleware to all routes within a group, you may use the Middleware method after defining the group. Middlewares are executed in the order they are listed:

Group(
    Get("/roles", controllers.RoleIndex),
    Get("/comments", controllers.CommentIndex),
).Middleware(First{}, Second{})

Subdomain Routing

Route groups may also be used to handle subdomain routing. The subdomain may be specified by calling the Domain method after defining the group:

Group(
    Get("/users", controllers.UserIndex,
).Domain("api.myapp.com")



Subdomains may be assigned route parameters just like route URIs, allowing you to capture a portion of the subdomain for usage in your controller:

Group(
    Get("/user/{id}", func(request inter.Request) inter.Response {
        account := request.Parameter("account").String()
        userId := request.Parameter("id").Int()
        //
    }),
).Domain("{account}.myapp.com")






 

In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path.

Route Prefixes

The Prefix method may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin:

Group(
    Get("/users", controllers.UserShow),
).Prefix("/admin")

// Matches "/admin/users"


 


Route Name Prefixes

The Name method may be used to prefix each route name in the group with a given string. For example, you may want to prefix all the grouped route's names with admin. The given string is prefixed to the route name exactly as it is specified, so we will be sure to provide the trailing . character in the prefix:

Group(
    Get("/users", controllers.UserShow).Name("users"),
).Name("admin.")

// Matches "admin.users"


 


Fallback Routes

Typically, unhandled requests will automatically render a "404" page via your application's exception handler. Using the Fallback function, you may define a route that will be executed when no other route matches the incoming request.

Group(
    Get("/users", controllers.UserShow).Name("users"),
    Fallback(func(request inter.Request) inter.Response {
        return outcome.Html("404 Page not found").Status(404)
    }),
)


 
 
 

The fallback route should always be the last route registered in a group.

Accessing The Current Route

You can get the current router from the container:

app.Make("route").(inter.Route)

Or get the route from the request:

request.Route()

You may use the Name, Method and Domain methods on the Route to access information about the route handling the incoming request:

name := request.Route().Name()
method := request.Route().Method()
domain := request.Route().Domain()
Contributors: Reindert Vetter, Vaggelis Yfantis