Documentation ¶
Overview ¶
Package render exposes high-performance HTML and JSON rendering functionality. Most use cases can use the Renderer without modification. More advanced use cases can customize template functions and error handling.
The renderer accepts a filesystem (fs.FS). In most cases, this will be a filesystem on disk. However, it accepts the FS interface for testing and embed purposes. Because embed does not perform hot reloading, you may want to use a different fs for development versus production:
//go:embed assets assets/**/* var _assetsFS embed.FS func AssetsFS() fs.FS { // In dev, just read directly from disk if v, _ := strconv.ParseBool(os.Getenv("DEV_MODE")); v { return os.DirFS("./assets") } // Otherwise use the embedded fs return _assetsFS }
The the renderer includes some prebuilt functions, including static asset parsing for CSS and Javascript files. The renderer assumes these files exist in a `static/css` and `static/js` directory at the root of the provided filesystem.
assets/ \_ static/ \_ css/ \_ js/
To render the include tags in a template:
{{ define "home" }} {{ cssIncludeTag "css/*.css" }} {{ jsIncludeTag "js/index.js" }} {{ end }}
Example ¶
package main import ( "context" "net/http" "sort" "testing/fstest" "time" "github.com/abcxyz/pkg/renderer" ) type Server struct { db map[string]any h *renderer.Renderer } func NewServer(ctx context.Context) *Server { // Normally this would come from the filesystem, but to make the test fit in a // single file... fsys := fstest.MapFS{ "users/index.html": &fstest.MapFile{ Data: []byte(` {{ define "users/index" }} <ul> {{ range .Users }} <li>{{ . | toUpper }}</li> {{ end }} </ul> {{ end }} `), Mode: 0o600, }, } h, err := renderer.New(ctx, fsys, renderer.WithDebug(true)) if err != nil { panic(err) } return &Server{ db: make(map[string]any), h: h, } } func (s *Server) Routes() http.Handler { mux := http.NewServeMux() mux.Handle("/users.html", s.HandleUsersIndex()) mux.Handle("/users.json", s.HandleUsersAPI()) return mux } func (s *Server) HandleUsersIndex() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s.h.RenderHTML(w, "users/index", s.users()) }) } func (s *Server) HandleUsersAPI() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s.h.RenderJSON(w, 200, s.users()) }) } func (s *Server) users() []string { users := make([]string, 0, len(s.db)) for k := range s.db { users = append(users, k) } sort.Strings(users) return users } func main() { s := NewServer(context.Background()) srv := &http.Server{ Addr: ":8080", Handler: s.Routes(), ReadHeaderTimeout: 2 * time.Second, } _ = srv.ListenAndServe() }
Output:
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Option ¶
Option is an interface for options to creating a renderer.
func WithOnError ¶
WithOnError overwrites the onError handler with the given function. This handler is invoked when an irrecoverable error occurs while rendering, but information cannot be sent back to the client. For example, if HTTP rendering fails after a partial response has been sent.
func WithTemplateFuncs ¶
WithTemplateFuncs registers additional template functions. The renderer includes many helpful functions, but some applications may wish to inject/define their own template helpers. Functions in this map take precedence over the built-in list. If called with multiple func maps or called multiple times with conflicting keys, the last key takes precedence. To delete an entry, supply a key with a nil value.
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
Renderer is responsible for rendering various content and templates like HTML and JSON responses. This implementation caches templates and uses a pool of buffers.
func NewTesting ¶ added in v0.4.0
NewTesting is a helper function to create a renderer suitable for injection in tests. It calls t.Fatal if setup fails.
func (*Renderer) RenderHTML ¶
func (r *Renderer) RenderHTML(w http.ResponseWriter, tmpl string, data any)
RenderHTML calls RenderHTMLStatus with a http.StatusOK (200).
func (*Renderer) RenderHTMLStatus ¶
RenderHTMLStatus renders the given HTML template by name. It attempts to gracefully handle any rendering errors to avoid partial responses sent to the response by writing to a buffer first, then flushing the buffer to the response.
If template rendering fails, a generic 500 page is returned. In dev mode, the error is included on the page. If flushing the buffer to the response fails, an error is logged, but no recovery is attempted.
The buffers are fetched via a sync.Pool to reduce allocations and improve performance.
func (*Renderer) RenderJSON ¶
func (r *Renderer) RenderJSON(w http.ResponseWriter, code int, data any)
RenderJSON renders the interface as JSON. It attempts to gracefully handle any rendering errors to avoid partial responses sent to the response by writing to a buffer first, then flushing the buffer to the response.
If the provided data is nil and the response code is a 2xx, the body will be empty. If the code is not a 2xx, the response will be of the format `{"error":"<val>"}` where val is the lowercase, JSON-escaped http.StatusText for the provided code.
If rendering fails, a generic 500 JSON response is returned. In dev mode, the error is included in the payload. If flushing the buffer to the response fails, an error is logged, but no recovery is attempted.
The buffers are fetched via a sync.Pool to reduce allocations and improve performance.
type TestingFatalf ¶ added in v0.4.0
TestingFatalf is an interface that is satisfied by testing.TB. It exists to avoid depending on the testing package at runtime.