Guide: Context responses and request binding
Every View is func(*tango.Context) error. Context wraps one request/response pair.
Reading the request
Param(name string) string— a named path parameter (from the route pattern's{name}placeholders), or""if absent.Query(name string) string— a named URL query parameter, or""if absent.Bind(dst any) error— decodes the request body as JSON intodst. There is no other content-type support (form values, multipart); readctx.Request()directly for that.
Writing the response
JSON(status int, payload any) error— setsContent-Type: application/json, writesstatus, and JSON-encodespayload.Redirect(url string) error— writes an HTTP redirect (302 Found) tourl.HTML(status int, tmpl *template.Template, name string, data any) error— renderstmpl's named templatename(viatmpl.ExecuteTemplate) againstdataand writes it astext/html; charset=utf-8. The render is buffered: nothing reaches the response unless execution fully succeeds, so a template error comes back as an ordinary returnederror— the same fixed-500 path any other view's error takes — rather than a response that already committed a status code followed by a truncated or malformed body.
Server-rendered HTML apps
HTML has no opinion on where templates come from or how they're parsed — that's ordinary Go, not a tanGO concern. A typical shape: embed templates once at package init, parse them into one *template.Template, and call ctx.HTML per request with whichever named template that request needs:
//go:embed templates/*.gohtml
var templatesFS embed.FS
var tmpl = template.Must(template.ParseFS(templatesFS, "templates/*.gohtml"))
func bookDetail(store *db.Store, meta model.ModelMeta) tango.View {
return func(ctx *tango.Context) error {
var book Book
if err := store.Get(ctx.Context(), meta, ctx.Param("id"), &book); err != nil {
return err
}
return ctx.HTML(http.StatusOK, tmpl, "book_detail", book)
}
}
A shared base layout composes the same way any html/template set does — a {{define "layout"}}...{{end}} template that other defined templates invoke via {{template "layout" .}}, all parsed together into the one *template.Template passed to every ctx.HTML call.
Escape hatches
Request() *http.RequestandResponseWriter() http.ResponseWriter— the raw request/response pair, for anything the helpers above don't cover (custom headers, streaming, cookies).Context() context.Context— the wrapped request's standardcontext.Context(for cancellation, deadlines, and passing todb.Storemethods, which all take acontext.Contextas their first argument). Note*tango.Contextitself does not implementcontext.Context— callctx.Context()explicitly wherever acontext.Contextis expected.
func createPost(store *db.Store, meta model.ModelMeta) tango.View {
return func(ctx *tango.Context) error {
var post Post
if err := ctx.Bind(&post); err != nil {
return err
}
if err := store.Create(ctx.Context(), meta, &post); err != nil {
return err
}
return ctx.JSON(201, post)
}
}