This section is about additional features of expression emission including:
Expression result is escaped before output depending on the current context to avoid injection attack. For example
<input value="@name">
Suppose name
value is "" onload="alert('Hack')
", if the output of @name
is not escaped, the result will be:
<input value="" onload="alert('Hack')">
Which is definitely not the template author wanted. If the expression output is escaped using html
scheme, then the result will be:
<input value="" onload="alert('Hack')">
Not in good look but avoid the html injection attack. Escape also solves a common problem of processing user input which contains special characters, e.g. for template
<script>
alert("@product.name")
</script>
If the product name contains ""
", then it will cause the javascript error at runtime. If the output is properly escaped, the issue will be addressed.
Not all template engine on the market address the expression escape issue properly, some of them even don't have a default escape scheme and require the template author to manually escape the expression.
Rythm, on the contrary, provides the state-of-art support on expression escape. Take a look at this example:
<html>
...
<p>@description</p>
...
<script>
alert("@description")
</script>
Suppose description
is "<h1>abc"xyz</h1>
", Rythm will generate:
<html>
...
<p><h1>abc"xyz</h1></p>
...
<script>
alert("<h1>abc\"xyz<\/h1>")
</script>
The above example shows clearly that Rythm use different escape scheme in different context (html and javascript). So here is what Rythm did, the initial escape context is set to html
, when Rythm encountered "<script>
" it switch the escape context to Javascript
until it reaches "</script>
" and then switch back to html
.
Rythm support the following escape schemes:
- html/xml
- javascript/js
- json
- csv
Usually template author does not need to explicitly specify the escape scheme because Rythm automatically switch escape scheme based on the parsing context as shown in the above example. However if in certain case it does require to set escape scheme explicitly, here is how to do it:
@args String s
// escape with html scheme
@s.escapeHtml()
@s.escapeHTML()
@s.escape("html")
@s().escapeHtml(s)
// escape with xml scheme
@s.escapeXml()
@s.escapeXML()
@s.escape("xml")
// escape with javascript scheme
@s.escapeJS()
@s.escapeJavaScript()
@s.escape("javascript")
@s.escape("js")
@s.escape("JS")
// escape with JSON scheme
@s.escapeJSON()
@s.escapeJson()
@s.escape("json")
// escape with CSV scheme
@s.escapeCSV()
@s.escapeCsv()
@s.escape("csv")
See also Set initial code type
When output an expression a common concern is how to deal with null
values. It is not unusual that we expect it output empty string ""
when null
value is expected. Which might result in verbose code for a simple expression like @foo.bar.zee
:
@if(null != foo && null != foo.bar) {@foo.bar.zee}
Fortunately, Rythm provides a feature called null safe expression, which allows you to use ?
to create expression without NPE concern. And the above code could be simplified as:
@foo?.bar?.id
The above expression will not throw out NullPointerException
if foo
or foo.bar
is null
.
@foo?.bar(@x?.y)
In some special case, template author might want to output default value if null
is found in an expression, and Rythm provides "elvis" operator in expression to handle that case:
@(foo ?: "not present")
The above code will output "not present" if variable foo
is null
.
@(foo?.bar ?: "not present")
Sometimes it needs some kind of transform operations to precess expression output. A typical example is to format a Date
typed variable:
@dueDate.format("yyyy-MM-dd")
Here the format
is called transformer, which accept a Date type object and format(transform) it using specified format pattern.
One or more Transformer could be applied to an expression to provide further processing. For example
@theString.capFirst().escapeJavaScript()