Controllers

Generator Arguments

Resolve typed controller action arguments from route params and request input.

By Guillermo Alvarez - Published Updated

Ask for route params

Controller actions can request generated arguments:

func (c *PostsController) Show(postID int) error {
    post, err := c.posts.Get(postID)
    if err != nil {
        return err
    }

    c.Set("post", post)
    return nil
}

String and integer arguments resolve from named route parameters in path order.

Use all params when needed

Slice arguments receive all named route params:

func (c *CommentsController) Show(params []string) error {
    teamID := params[0]
    postID := params[1]
    commentID := params[2]
    _ = teamID
    _ = postID
    _ = commentID
    return nil
}

For /teams/{team_id}/posts/{post_id}/comments/{comment_id}, the slice order matches the path order.

Generate request input

Add a GenX method when an action needs a custom type:

type PostInput struct {
    Title string
}

func (c *PostsController) GenPostInput(
    r *http.Request,
) (PostInput, error) {
    if err := r.ParseForm(); err != nil {
        return PostInput{}, err
    }
    return PostInput{Title: r.Form.Get("title")}, nil
}

func (c *PostsController) Create(input PostInput) error {
    post, err := c.posts.Create(input.Title)
    if err != nil {
        return err
    }
    c.Set("post", post)
    return nil
}

Generators return T or (T, error).

Compose generated values

Generators may depend on standard arguments and other generated types:

func (c *PostsController) GenPost(
    postID int,
) (postservice.Post, error) {
    return c.posts.Get(postID)
}

func (c *PostsController) Show(post postservice.Post) error {
    c.Set("post", post)
    return nil
}

Generated values are cached for the current request, so one generated type is created only once even if multiple arguments need it.

Let errors flow normally

Generator errors use the same path as action errors:

func (c *PostsController) GenPostInput(
    r *http.Request,
) (PostInput, error) {
    if r.ContentLength > 1<<20 {
        return PostInput{}, lazycontroller.Error(
            http.StatusRequestEntityTooLarge,
            fmt.Errorf("form too large"),
        )
    }
    // ...
}

The action is not called when argument resolution fails.