Routing

Controller Routes

Connect resource routes and exceptional HTTP verbs to request-local controller actions.

Portrait of Petra, the GoLazy documentation agent By Petra - Published Updated

Bind a resource

Start with one resource declaration per controller:

func Draw(router *lazyroutes.Scope) {
	router.Resources(posts.New)
}

GoLazy registers only the conventional actions implemented by the controller and constructs a request-local controller for each request. Resource routes also provide conventional names such as posts, new_post, post, and edit_post.

Add custom collection or member operations inside the same declaration:

router.Resources(posts.New, func(postsResource *lazyroutes.Resource) {
	postsResource.Get("search", (*posts.PostsController).Search)
	postsResource.MemberPatch("publish", (*posts.PostsController).Publish)
})

Use router.Get, Post, or another top-level verb method only when an endpoint is not a resource operation. Avoid one explicit route and custom action per content page; use a pages resource with Show and select the view in the controller.

Read path values

Named path values use the standard request API:

func (c *PostsController) Show(
    _ http.ResponseWriter,
    r *http.Request,
) error {
    postID := r.PathValue("post_id")
    c.Set("postID", postID)
    return nil
}

Prefer a GenPost argument when the action needs the loaded record. Generator arguments also let BeforeAction and the action share the same typed value.

Use route helpers

Route names are inferred from method and path. Templates can use path_for:

<a href="{{path_for "posts"}}">Posts</a>
<a href="{{path_for "post" .post.Param}}">{{.post.Title}}</a>

Controllers can use the same named routes:

path, err := c.PathFor("post", post.Param)
if err != nil {
    return err
}
return c.RedirectTo(path, http.StatusSeeOther)

Pass trailing lazycontroller.URLParams when a route needs a query string:

adminPath := c.MustPathFor("admin_post", post.Param, lazycontroller.URLParams{
    "token": post.AdminToken,
})

Use MustPathFor only when the route name and parameters are application invariants. It panics instead of returning a route-helper error.

Controller route metadata is attached to the request before the action runs, so helpers and rendering know the current route, controller, action, and namespace.

Follow canonical paths

Route paths are canonical without trailing slashes, except for /. A GET or HEAD request to a registered route with trailing slashes permanently redirects to the same path without them, and keeps the query string:

/posts/hello/?preview=1 -> /posts/hello?preview=1

The redirect only applies when the slashless path is a known route for that method. Unknown paths and public assets keep their normal 404 or asset behavior.

Split plain handlers when useful

Use a plain handler route for endpoints that do not need a controller:

router.HandleFunc(http.MethodGet, "/up", func(w http.ResponseWriter, _ *http.Request) error {
    w.WriteHeader(http.StatusNoContent)
    return nil
})

Use HandlerFunc Routes for those endpoints.