Drops
DotLiquid is focused on making your templates safe. It does this by making sure templates can only access properties and methods that have been specifically enabled. This is best explained by example.
If you execute the following code, it might not produce the result you expect.
public class User
{
public string Name { get; set; }
}
Template template = Template.Parse("{{ user.name }}");
string result = template.Render(Hash.FromAnonymousObject(new
{
user = new User { Name = "Tim" }
}));
You could reasonably expect the output to be Tim. However, it will actually be a blank string. The explanation follows.
Explanation
DotLiquid, by default, only accepts a limited number of types as parameters to the Render method - including the .NET primitive types(int, float, string, etc.), and some collection types including IDictionary, IList and IIndexable (a custom DotLiquid interface).
If it supported arbitrary types, then it could result in properties or methods being unintentionally exposed to template authors. To prevent this, DotLiquid uses Drop objects. Drops use an opt-in approach to exposing object data.
You can derive your own Drop class, and pass that to Template.Render instead of the original User object:
public class User
{
public string Name { get; set; }
public string Email { get; set; }
}
public class UserDrop : Drop
{
private readonly User _user;
public string Name
{
get { return _user.Name; }
}
public UserDrop(User user)
{
_user = user;
}
}
Template template = Template.Parse("Name: {{ user.name }}; Email: {{ user.email }};");
string result = template.Render(Hash.FromAnonymousObject(new
{
user = new UserDrop(new User
{
Name = "Tim",
Email = "me@mydomain.com"
})
}));
The result is Name: Tim; Email:, so the attempt to call the email property was ignored, since this is not exposed by UserDrop.
POCO classes
As of v1.7.0, DotLiquid provides an API to register specific types as "safe", which means you can use types that don't inherit from Drop. Read more on the format.
Tips
-
By default, DotLiquid uses a Ruby naming convention for
Dropproperties. So if yourDropobject contains CamelCased properties, they will contain underscores when exposed in your template. For example,ProductIdon your drop becomesproduct_idin your template. If you want to change this behaviour, you can setTemplate.NamingConventiontonew NamingConventions.CSharpNamingConvention(). -
Exceptions thrown by your Drop objects will result in the general message
Liquid error: Exception has been thrown by the target of an invocation.. You can make DotLiquid throw the original exception by using theRenderoverloads that take aRenderParametersinstance and settingRethrowErrors = true.
Taking it further
Drops are quite flexible. You can also use them to provide a "catch-all" method:
public class CatchAllDrop : Drop
{
public override object BeforeMethod(string method)
{
return "method: " + method;
}
}
Template template = Template.Parse(" {{ catchall.unknown }} ");
string result = template.Render(Hash.FromAnonymousObject(new { catchall = new CatchAllDrop() }));
The result is method: unknown.
For a more hands-on explanation of all the capabilities of Drops, have a look at the Drop tests within DotLiquid's unit test suite.