Chapter 6. HTML and email template patterns

This chapter covers

  • Adding functionality inside templates
  • Nesting templates
  • Using template inheritance
  • Rendering objects to HTML
  • Using email templates

When you’re programmatically creating text or HTML responses in many programming environments, you need to seek out the right library to handle the HTML. Go handles this a little differently. In the standard library, Go provides template handling for both text and HTML. The HTML handling is built on top of the text template engine to add HTML-aware intelligence.

Although the standard library enables you to work with HTML templates, it stops short of having too many opinions. Instead, it provides a foundation along with the ability to extend and combine templates. For example, you could nest templates or have a template inherit from another one. This simple and extensible design allows you to use many common template patterns.

In this chapter, you’ll learn how to extend the functionality inside HTML templates and techniques to use templates together. Along the way, we offer tips, including some related to performance, that can speed up applications. For example, you’ll learn where you can parse a template that can save overall processing time. You’ll then learn how to use text templates when you send email messages.

6.1. Working with HTML templates

The html and html/template packages in the standard library provide the foundation for working with HTML. This includes the ability to work with variables and functions in the templates. The html/template package is built on the text/template package, which provides general text template handling. HTML, being text, can use the text handling. The advantage of the html/template package over the text/template package for HTML is the context-aware intelligence that saves developers work.

Although these packages provide the groundwork to working with HTML, they stop short of having many opinions. What you can do and how you should structure your HTML templates is left to the application authors. In the next couple of techniques, you’ll look at patterns that will be helpful in extending the template packages for your own applications.

6.1.1. Standard library HTML package overview

Before you look at those patterns, you need to look at how the packages in the standard library work. Whereas the html package provides only a couple of functions to escape and unescape strings for HTML, the html/template package provides a good foundation for working with templates. First, let’s look at a basic HTML template in the next listing.

Listing 6.1. A simple HTML template: simple.html

This listing illustrates the basics of a template. Aside from the actions (also called directives), which are enclosed in double curly brackets, the template looks like a normal HTML file. Here the directives are to print a value passed into the template, such as printing the passed-in title. The next step is to call this template from code and pass it the values to fill in for {{.Title}} and {{.Content}}, as shown in the next listing.

Listing 6.2. Using a simple HTML template: simple_template.go

This simple application takes some data and displays it via a simple web server, using the template from listing 6.1. The html/template package is used here instead of the text/template package because it’s context-aware and handles some operations for you.

Being context-aware is more than knowing that these are HTML templates. The package understands what’s happening inside the templates. Take the following template snippet:

<a href="/user?id={{.Id}}">{{.Content}}</a>

The html/template package expands this intelligently. For escaping purposes, it adds context-appropriate functionality. The preceding snippet is automatically expanded to look like this:

<a href="/user?id={{.Id | urlquery}}">{{.Content | html}}</a>

The variables (in this case, .Id and .Content) are piped through appropriate functions to escape their content before turning it into the final output. Escaping turns characters that could be seen as markup and that alter the page structure or meaning into references that display properly but don’t alter the structure or meaning. If you were using the text/template package, you would need to add the escaping yourself.

The context-aware escaping is built around a security model in which template developers are considered trusted, and user data, injected via variables, is considered untrusted and should be escaped. For example, if an application user input the string <script>alert('busted pwned')</script>, and you displayed that string through the HTML template system, it would be escaped and &lt;script&gt;alert(&#39 ;busted pwned&#39;)&lt;/script&gt; would be rendered in the template. This is safe to display to users and avoids a potential cross-site scripting (XSS) vulnerability.

When you want a variable to be rendered as is, without being escaped, you can use the HTML type in the html/template package. Technique 35 provides an example that uses the HTML type to inject data into a template and avoid escaping.

The following four techniques look at ways to extend the built-in template system, allowing you to use common template patterns.

6.1.2. Adding functionality inside templates

Templates in Go have functions that can and will be called from within them. As you just saw, the intelligence in the HTML templates adds escaping functions in the right place for you. These functions are where complex functionality is handled in the templates. Out of the box, the template packages provide fewer than 20 functions, and several are to support this intelligence.

For example, consider one of the built-in functions, whose implementation is provided by fmt.Sprintf, is printf. The following code shows its use inside a template:

{{"output" | printf "%q"}}

The snippet takes the string output and passes it into printf by using the format string %q. The output is the quoted string output.

Technique 32 Extending templates with functions

Although templates provide quite a few features, often you need to extend them with your own functionality. The need to add features isn’t uncommon or uncalled for. For example, we’ve often seen the need to display a date and time in an easy-to-read format. This common request could easily be implemented as part of the template system. This is just one common example, and template systems can be extended in many cases.

Problem

The built-in functions in the templates don’t provide all the functionality you need.

Solution

Just as Go makes functions available in templates (such as fmt.Sprintf being available in templates as printf), make your own functions available.

Discussion

You can display information in templates in various ways. Although the way data is generated should be kept in the application logic, the way it’s formatted and displayed should cleanly happen in a template. Presenting date and time information is a good example. In an application, the time information should be stored in a type, such as time.Time. When displayed to users, it could be displayed in a myriad of ways.

Go actions, the data and commands enclosed in double curly brackets, can have commands that act on the data. These commands can be chained into pipelines separated by a |. This is the same idea as using pipes from a UNIX-based command-line interface (CLI). Go provides an API to add commands to the set available to a template. The limited set of commands that comes out of the box doesn’t need to be the only set available to your templates. The following listing takes a template and adds the capability to display formatted dates.

Listing 6.3. Add template functions: date_command.go

Rather than referencing an external file, this HTML template is stored as a string in a variable. Inside the template, the data in Date is passed through the template function dateFormat with a specified format string before becoming part of the output. It’s important to know that the piping mechanism passes the output from one item in the pipeline into the next item in the pipeline as the last argument.

Because dateFormat isn’t one of the core template functions, it needs to be made available to the template. Making custom functions available in templates requires two steps. First, a map needs to be created in which names to be used inside the template are mapped to functions in Go. Here the function dateFormat is mapped to the name dateFormat. Although the same name is used for both the Go function and name available inside the template, that doesn’t have to be the case. They can be different names.

When a new template.Template instance is created, the function map (here, named funcMap) needs to be passed into Funcs to make the new function mapping available to templates. After this happens, templates can use the functions. Before using the template, the final step is to parse the template into template.Template.

From here, the template instance is used normally. The data structure is defined, in this case, by an anonymous struct, with the data to pass in as a key-value mapping. The data structure is passed into Execute along with the io.Writer to output the rendered template. In this case, when dateFormat is encountered in the template, the format of Jan 2, 2006 is passed in, followed by the time.Time instance. The instance of time.Time is converted to a string following this format.

Note

The date and time used in format strings needs to be specific and is detailed in the package documentation at http://golang.org/pkg/time/#Time.Format.

If you’re going to apply the same function set to numerous templates, you can use a Go function to create your templates and add your template functions each time:

func parseTemplateString(name, tpl string) *template.Template {
     t:= template.New(name)
     t.Funcs(funcMap)
     t = template.Must(t.Parse(tpl))
     return t
}

This function could be repeatedly used to create a new template object from a template string with your custom template functions included. In listing 6.3, this could be used inside the serveTemplate function instead of parsing the template and adding the template functions there. Using a Go function to configure your templates for you could be done with files as well.

6.1.3. Limiting template parsing

Parsing templates that were originally in text into type instances is a bit of work for a Go application. Parsing a template turns a string of characters into an object model with a variety of nodes and node types that Go knows how to use. The parser in the text/template/parser package sits behind functions in the template packages such as Parse and ParseFiles. Unless you work directly with the parser, which isn’t recommended, it’s easy to miss all the work going on behind the functions you use.

Methods such as the following technique allow you to avoid extra work by using a parser that can speed up your application.

Technique 33 Caching parsed templates

Go applications, as servers that respond to multiple requests, can generate many responses to requests from many different clients. If for each response the application has to parse a template, a lot of duplicate work is going on. If you can eliminate some of that work at response time, you can speed up your application’s performance.

Problem

You want to avoid repeatedly parsing the same template while an application is running.

Solution

Parse the template, store the ready-to-use template in a variable, and repeatedly use the same template each time you need to generate the output.

Discussion

Instead of parsing the template in the http handler function, which means parsing the template each time the handler function runs, you can move the parsing out of the handler. Then you can repeatedly execute the template against different datasets without parsing it each time. The following listing is a modified version of listing 6.2 that caches the parsed template.

Listing 6.4. Caching a parsed template: cache_template.go

Instead of parsing the template in the handler function, as listing 6.2 does, the template is parsed once when the package is initialized. When the http handler function is executed, the template is executed normally.

As the benchmark test examples from chapter 5 showed, parsing a template and reusing the parsed template is faster than parsing each time. This is a subtle, simple way to speed up application responses.

6.1.4. When template execution breaks

All software has the potential to fail. Template execution is no exception. When template execution fails, an error is returned. But in some cases, template execution can fail and partial output is displayed to the end user. This can provide an experience you want to avoid.

Technique 34 Handling template execution failures

When templates are executed, the output is written as it walks through the template. If a function is called, causing an error midway through the template, an error will be returned and execution will stop. But the part of the template before the error would already be displayed to end users.

Problem

When an error happens while executing a template, you want to catch the error before anything is displayed to the user. Instead of displaying the partially broken pages, display something more appropriate, such as an error page.

Solution

Write the output of the template to a buffer. If no errors occur, write the output of the buffer to end users. Otherwise, handle the error.

Discussion

Templates should be fairly unintelligent. They display data, and functions can be used to format the data. Any errors in the data should be handled before templates are used with the data, and the functions called within the templates should be for display purposes. This keeps the separation of concerns in place and limits failures when templates are executed, which is important for streaming.

Streaming responses is useful. When you execute a template to a response writer, end users start to receive the page more quickly. When you buffer a response, there’s a delay in end users receiving it. End users expect native desktop performance out of web applications, and streaming responses helps achieve that. When possible, write to output.

But at times, the optimal case doesn’t work out. If executing templates carries a potential for errors, you can write the output of the template to a buffer. If errors occur, they can be handled before displaying anything to the end users. The following listing builds on listing 6.4 to introduce a buffered output.

Listing 6.5. Buffering a template response: buffered_template.go

When the template is executed, a buffer is written to instead of http.ResponseWriter. If any errors occur, those are handled before copying the contents of the buffer to the output http.ResponseWriter.

6.1.5. Mixing templates

The foundation of generating HTML output is the html/template package. It handles safely generating HTML output. But the documented use cases are simple. When building applications, you’ll often want to mix templates together, have patterns for reusing and managing them, cache generated output, and more. In the following patterns, you’ll see three ways to work with templates, built on top of the standard library, allowing you to use more-complex template handling. These patterns are template nesting, extending a base template through inheritance, and mapping a data object to a specific template (for example, a user object being mapped to a user template).

Technique 35 Nested templates

Sharing and reusing sections of templates, like code reuse, is a common need. If you have an application with numerous web pages, you’ll typically find that most elements are common across pages, and some elements are custom.

Problem

You want to avoid duplicating the common sections of HTML markup in each template and the maintenance burden that goes along with that. Like the software you’re developing, you want to take advantage of reuse.

Solution

Use nested templates to share common sections of HTML, as shown in figure 6.1. The subtemplates enable reuse for sections of markup, cutting down on duplication.

Figure 6.1. Using nested subtemplates to share common template code

Discussion

The template system in Go is designed to handle multiple templates and allow them to work together. A parent template can import other templates. When the parent is executed to render the output, the subtemplates are included as well. The following listing shows how this works, touching on some important nuances.

Listing 6.6. Index template including head template: index.html

This nested template example starts out with index.html. This is similar to the simple template from listing 6.1. The difference is that instead of a <head> section, there’s a directive to include another template.

The directive {{template "head.html" .}} has three parts. template tells the template engine to include another template, and head.html is the name of that template. The final part is the . after head.html. This is the dataset to pass to the template. In this case, the entire dataset from the parent template is passed to this template. If a property on the dataset contained a dataset for a subtemplate, that could be passed in (for example, if {{template "head.html" .Foo}} were used, the properties on .Foo would be the ones available inside head.html). See the following listing.

Listing 6.7. Head template included in the index: head.html

When head.html, as seen in listing 6.7, is invoked by index.html, the entire dataset is passed in. When Title is used, it’s the same Title used in index.html, as head.html has access to the entire dataset.

The next listing brings the example together.

Listing 6.8. Using the nested templates: nested_templates.go

This listing starts by parsing the two templates to the same template object. This allows head.html to be accessible to index.html when it’s executed. When the template is executed, ExecuteTemplate is used so that the template name to execute can be specified. If Execute had been used, as in the previous listings, the first template listed in ParseFiles would be used. ExecuteTemplate provides control over the template file when multiple ones are available.

Technique 36 Template inheritance

Many template systems implement a model with a base template and other templates that fill in the missing sections of the base template. They extend the base template. This is different from the previous technique, in which subtemplates were shared among a group of different top-level templates. In this case, the top-level template is shared.

Problem

You want to have a base template and have other templates extend the base template. The templates would have multiple sections that can be extended.

Solution

Instead of thinking of a file as a template, think of sections of a file as templates. The base file contains the shared markup and refers to other templates that haven’t yet been defined, as shown in figure 6.2. The templates extending the base file provide the missing subtemplates or override those in the base. After they’re combined, you have a fully working template with a shared base.

Figure 6.2. A shared based template

Discussion

The template system enables some inheritance patterns within templates. It doesn’t represent the full range of inheritance available in other template systems, but patterns can be applied. The following listing shows a base template for others to inherit from.

Listing 6.9. A base template to inherit from: base.html

Instead of the entire file being a template, the file contains multiple templates. Each template starts with a define or block directive and closes with an end directive. The block directive defines and immediately executes a template. This file opens by defining a base template. The base template, which can be referred to by name, invokes other templates but doesn’t necessarily define them. Templates that extend this one, such as listing 6.10, will need to fill in the missing templates. In other cases, you may have a section with default content that you want to allow to be overridden by an extending template. Some sections may be optional. For those sections, you can create empty templates to be used by default.

Note

The block directive and ability to redefine template sections that have content was introduced in Go 1.6. Prior to this, you couldn’t redefine templates that had content.

Listing 6.10. Inheriting required sections: user.html

Templates extending the base need to make sure all of the subtemplates without a default are filled out. Here the title and content sections need to be defined because they’re required. You’ll notice that the optional sections with empty or default content defined from listing 6.9 don’t need to have sections defined.

The following listing showcases filling in an optional template in addition to the required sections.

Listing 6.11. Inheriting with optional section: page.html

Here the styles template is defined. This overrides the default supplied in listing 6.9.

The following listing brings the templates together.

Listing 6.12. Using template inheritance: inherit.go

This listing starts by creating a map to hold the templates. Each template is stored separately from the others. The map is populated with the template instances by using a key for the template name. When the templates user.html and page.html are loaded, the base.html file is loaded with each of them. This allows for the inheritance in each case.

Preparing to render a page happens in a similar manner to the normal template usage. A dataset is defined and populated. When it’s time to render a response, the template to use is selected from the map of templates and the base template is invoked. The base is the root of the page and needs to be the one invoked. It will invoke the subtemplates defined in the inheritance.

Technique 37 Mapping data types to templates

The previous two template techniques rendered all the output together. A dataset consisting of the entire page needs to be passed in, and the template setup needs to handle the variations to the full page.

An alternative approach is to render parts of the page, such as a user object instance, on its own and then pass the rendered content to a higher-level template. The higher-level template doesn’t need to know the data type or how to render it. Figure 6.3 represents this concept.

Figure 6.3. HTML rendered objects passed into the template

Problem

You want to render an object to HTML and pass the rendered object to a higher-level template, where it can be part of the output.

Solution

Use templates to render objects as HTML. Store the HTML in a variable and pass the HTML to higher-level templates wrapped in template.HTML, marking it as safe HTML that doesn’t need to be escaped.

Discussion

There are a couple reasons to have multiple rendering steps. First, if part of the page is expensive to generate a dataset for or render to HTML, it’s worth not repeating when each page is generated.

For example, imagine you have a directory listing for a user. The listing contains information about a user and their activity that can be viewed by many other users. Obtaining the dataset to render would require multiple data source lookups. If that were cached, you could skip loading this information each time the page is viewed.

Caching this dataset would still require the dataset being rendered on each page load, and you’d need to store a complicated dataset somewhere. Rendering the data would mean the template package handles rendering in the right format and making sure everything is properly escaped. If the cache were instead populated with a rendered HTML snippet to reuse each time, more work on each page generation would be skipped due to better caching.

In a second case, say you have applications with complicated logic and have many pages to render. You could have many templates containing a lot of duplicate markup. If each template were instead scoped to render one thing—whether it be the main content, a piece of the sidebar, or the page wrapper—the templates could be easier to manage.

The following listing shows how to render an object from a template, store the HTML, and later inject it into another template.

Listing 6.13. A Quote object template: quote.html

This template, quote.html, is associated with a Quote object. The template is used to render the Quote object as HTML and has Quote object fields to render. You’ll notice there are no other elements for a complete page here. Instead those are part of index.html, shown in the following listing.

Listing 6.14. A generic page wrapper: index.html

The index.html file is a template for the page wrapper. It contains variables that make sense in the scope of a page. The variables printed out aren’t specific to a user or anything else. The following listing pulls this together.

Listing 6.15. Bringing the templates together: object_templates.go

This code starts out in a fairly typical manner. It begins by parsing the two templates, quote.html and index.html, into a variable. In this case, you have two data structures for use. The first is for the output of a web page. The second is Quote, which can be converted to HTML.

To create a piece of content separate from generating the page, a quote is instantiated as part of the main function. Quote is passed into ExecuteTemplate along with the quote.html template to render the quote as HTML. Instead of writing the template to output, the template is written to Buffer. Then Buffer is converted to a string and passed into template.HTML. The html/template package escapes most of the data sent into it. An exception to that is template.HTML, which is safe HTML. Because the content was generated from a template that performed escaping, you can store the output of the quote.html template as safe HTML to use later.

In the Page type, you’ll notice that the Content property is the type template.HTML. When the dataset used to generate the page is created, the HTML generated from the Quote object is set as the Content. When the index.html template is invoked with the dataset, the template system knows to skip escaping anything of the type template.HTML. The quote HTML is used as is. This provides a clean way to store and pass around safe HTML.

Warning

User input HTML should never be considered safe. Always escape user input information, such as information gathered from a form field, before presenting.

6.2. Using templates for email

Email is one of the staples of modern communication. It’s often used for service notifications, registration verification, and more. Even services looking to take over where email has dominated will end up using it in some capacity.

The Go standard library doesn’t provide a special template package for email as it does for HTML. Instead, the text and html template packages provide what you need to send text and HTML email.

Technique 38 Generating email from templates

Email is one of the places templates can be used. Sometimes email is generated as text and other times as HTML. These happen to be the two template packages provided by the standard library.

Problem

When creating and sending email, you want to incorporate templates.

Solution

Use the template packages to generate the email text into a buffer. Pass the generated email in the buffer to the code used to send the email, such as the smtp package.

Discussion

Templates can be used for a wide variety of things, and email messages are a great place to use them. To illustrate this, the following listing creates email messages from a template and sends them using the net/smtp package.

Listing 6.16. Send email from a template: email.go

This code sends a simple email generated from a template. You’ll notice the listing is using the text/template package instead of the html/template package used in the previous listings in the chapter. The html/template package is built on top of the text/template package. It provides HTML-specific features such as context-aware escaping on top of the text/template package.

Using the text/template package means the injected properties (for example, .Body) aren’t escaped. If you need to escape anything injected into the template, you can use escape functions from the text/template package.

When you execute the template with a dataset, pass in a buffer to store the rendered template. The buffer provides the source of the content to send from the mail client.

This concept can be expanded to send a variety of email in a variety of ways. For example, you could use the html/template package to send HTML email. Or you could combine this with the other template techniques to create complex templates.

6.3. Summary

Using and extending template patterns for both HTML and email allows you to handle complexity in a more maintainable manner. This is useful as complexity grows within an application. These patterns include the following:

  • Extending the functionality within templates through the use of piping commands.
  • Caching and buffering templates.
  • Having reusable sections within templates that can be shared across templates. For HTML templates, this includes having reusable sections such as a header or footer.
  • Starting with a base or master template that’s extended by other templates used.
  • Mapping templates to objects, such as a user template for a user object, and rolling the templates up into a page-level output.
  • Generating email output with templates.

In the next chapter, you’ll explore serving static content and handling user input from HTML forms. This includes serving files such as JavaScript files, stylesheets, and images in several ways. You’ll cover HTML form-handling patterns that can take the pain out of working with user input, especially when it comes to files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.208.97