RazorTemplateEngine parser produces unusual syntax tree - c#

I was looking for a way to to reuse my MVC Razor views as javascript templates for client side rendering, and found this library (Razor Client Template) which parses razor views into javascript functions.
It doesn't play ball with Razor engine version 2, and a little digging shows this is because the Razor engine's syntax tree has been overhauled. In an attempt to modify things, I've found some weird results from the RazorViewEngine parser.
I have a very simple view, like so:
#model Justis4.Models.EntityModel
<div>
#Model.PropertyOne
#Model.PropertyTwo
</div>
The razor client template library starts off with:
var host = new RazorEngineHost(newCSharpRazorCodeLanguage());
var engine = new RazorTemplateEngine(host);
var parserResults = engine.ParseTemplate(razorTemplate); //from string reader
var doc = parserResults.Document;
and then goes off to start parsing the resulting syntax tree into a javascript function.
When I debug through the syntax tree, I see some odd stuff. As I understand it, the Razor engine splits the view into "blocks" and "spans" of different types. But as in the picture, the model declaration at the top has been parsed as markup, not code. There are similar oddities, and as a result, the rest of the parsing to javascript fails.

The standard razor parser does not recognise the #model keyword. The #model keyword comes from the MvcCSharpRazorCodeParser class in the System.Web.Mvc assembly.
The main functionality for this comes from SetModelTypeCodeGenerator. This class makes use of the core razor engine class, SetBaseTypeCodeGenerator to change the base type for the razor view from the default WebViewPage to WebViewPage<Model>.
Solutions
Use the #inherits keyword instead e.g. #inherits WebViewPage<EntityModel>.
Or, add a reference to System.Web.Mvc and use a custom language change the code parser to MvcCSharpRazorCodeParser. You will need to set the RazorEngine.DefaultBaseClass property to a non-generic version of the base class you intend to use, e.g. for WebViewPage<T> you would set engine.DefaultBaseClass = "System.Web.Mvc.WebViewPage".
class MvcCSharpRazorCodeLanguage : CSharpRazorCodeLanguage
{
public override ParserBase CreateCodeParser()
{
return new MvcCSharpRazorCodeParser();
}
}

Related

Creating partial view with dynamic content

I understand when I create a view, I shouldn't be putting any code in there besides html and the data from the model/controller, which is what I've done so far.
But lets say there is a snipped of dynamically generated html that can be used in multiple views, I'm guessing this would be a partial view that goes in the Shared folder in the project. But since it's a partial view, that has no absolute controller to handle it's propagation of dynamic data (from db), how would I call, and where would I code the propagation of data from the db into the view (or model?), if lets say the partial view was to dynamically render content for table.id=n, etc.
I'm fairly to new and working off a tutorial in .net, trying to figure out how to do this. Anyone know how it's done? Hope the question makes sense.
You can always define a model for the partial.
And you can render the partial from the container view passing a dinamically populated instance of its model:
<!-- index.cshtml -->
<h1>Feed Upload</h1>
<div id="uploader">
#Html.Partial("~/Views/Shared/Controls/_FileUploader.cshtml", new FileUploaderModel() { UploaderClassName = this.Model.UploaderClassName })
</div>
In this simple example I call the partial _FileUploader.cshtml from the index.cshtml using the #Html.Partial() method, passing a new model instance that specifies the UploaderClassName value.
Edit
The this.Model.UploaderClassName refers to the container's model and it is initialized inside the container's controller business. Of course the container's controller can run any data access logic to grab dynamic data from the db and pass them to the partial's model.
Have a look at MSDN, and at this article.
Assuming you are using the razor view engine, you can put an .cshtml file in the App_Code folder with helper functions.
The syntax is like this:
#helper FormatDate(DateTime date)
{
#date.ToShortDateString()
}
You call it like this (assuming the file is Utility.cshtml)
#Utility.FormatDate(Patient.DOB)
Because you can pass parameters to a helper, you can pass any type you need, including complex objects.
I recently published a nuget package to do this very thing. It's called Dynamic MVC.
http://dynamicmvc.com
You can look at the source code on codeplex.
https://dynamicmvc.codeplex.com
The way I did this was to use the ModelMetadata engine built into MVC to allow me get the value for any property in a weakly typed fashion. The ModelMetadata engine originally came from ASP.net Dynamic Data and was ported over to MVC in MVC2. It works great for this kind of situation.

Using #model dynamic in a Partial nested in a subfolder throws RuntimeBinderException

I'm having some problems finding a solution for this, though it seems straight-forward enough, and I imagine someone else must have run into this issue before.
Using MVC/Razor 4, I am trying to render a partial using a dynamic model.
To organize things, I have placed my partials in a sub-folder within the same view folder.
When the partial in question is moved to the sub-folder, it throws a RuntimeBinderException with an exception message saying that 'object' does not contain a definition for 'Id' (the parameter I am trying to access).
This works perfectly fine when the partial is located in the same folder.
This structure works fine
Views/Orders/Details.cshtml
Views/Orders/_PartialWithDynamicModel.cshtml
This structure causes the exception
Views/Orders/Details.cshtml
Views/Orders/MyPartials/_PartialWithDynamicModel.cshtml
CODE
Details.cshtml
#Html.Partial("MyPartials/_PartialWithDynamicModel", new { Id = 54 } )
_PartialWithDynamicModel.cshtml
#model dynamic
# { //The following line throws the RuntimeBinderException
int id = Model.Id; }
Any thoughts? If I move the partial into the same folder as the view, everything works fine.
Your problem is that you can't pass an anonymous type to an object in a separate assembly. They are created as "internal" types, and thus cannot be passed externally. Views are generated dynamically at runtime into their own assemblies.
Instead, use an ExpandoObject, like this:
#{ var myExpando = new ExpandoObject();
myExpando.Id = 54; }
#Html.Partial("MyPartials/_PutOnHoldForm", myExpando)
A better choice, however, would be to just pass a ViewDataDictionary, or perhaps use Tuples.
There is also the DynamicViewPage extension in the MVC futures project that allows you to do this as well without the expand object.
http://weblogs.asp.net/imranbaloch/using-the-features-of-asp-net-mvc-3-futures
(note, it says MVC3, but there is an MVC5 version of futures in Nuget)

How can i isolate Razor Views?

Is there a way to expose Razor syntax and (custom) helpers to people , but say ... not allow them to create code blocks or , to only limit them in the usage of the helpers and to not give them the power to execute pure C# code in the views ?
Any ideas and pointers to similar solutions are welcome !
update:// I would like to give the users the power to write their own HTML and access only to a list of html helpers. Mostly the default ones and the ones i create.
For example i do not want them to be able to execute code within #{ //code } blocks and
Also no using and #model ( not sure about this one)
only have access to #Html.* #if else for foreach
or better yet , give them access only to specific namespaces (this just a thought tho)
update://
After some testing , i found out that RazorEngine does as close as to what i'm trying to do : run the views in isolated environment and add access to specific namespaces.
I would not recommend you doing that. There simply is not an easy and reliable way to give them this ability without compromising the security of your site. If you trust your users then you could do it. If you don't then a templating engine such as DotLiquid is something far more appropriate for this purpose.
There is a project called RazorEngine, built upon Microsoft's Razor, that allows you to parse that syntax without being in the context of returning an MVC view. Here's how it's used:
string template = "Hello #Model.Name! Welcome to Razor!";
string result = Razor.Parse(template, new { Name = "World" });
You can also specify a customized template base, which should allow you to define only the Html Helpers you want to expose to your users:
Razor.SetTemplateBase(typeof(HtmlTemplateBase<>));
string template =
#"<html>
<head>
<title>Hello #Model.Name</title>
</head>
<body>
Email: #Html.TextBoxFor(m => m.Email)
</body>
</html>";
var model = new PageModel { Name = "World", Email = "someone#somewhere.com" };
string result = Razor.Parse(template, model);
you may try to change razor view engine and related classes to check for disallowed situations.
When source is generated (view engine generates a source file to compile ), you have to check it manually (by parsing c# or vb.net code). It is possible, but not feasible (really).
Even if you have managed to parse and check code, you have to identify your code (which is allowed) and customer code (which has restrictions).
At the end you have to accept the fact you can not really disallow anything other than using another template engine.
because
Your customers will find a way to make their views look like yours.
You cannot limit most basic required features like var r = new Random();
You cannot estimate what most basic requirements are
you cannot say No to your customers when they need to use their custom libraries
By the way, you may try another thing. Write a virtual path provider, and convert customer templates written in AviatrixTemplate when requested by runtime. By using this route, you still use razor engine, loose only a slight time when converting (it is one time only). But your AviatrixTemplate won't be hilighted, and you still need to check for disallowed code.
PS: a basic loop may give your users more then you want. for example following code allows creation of a class and call it for one time. they may use fully qualified class name or may use Activator.CreateInstance.
#for (var r = new Random(); r != null; r = null)
{
#r.NextDouble()
}
just do not bother.
I have never done this before, but it sounds like you want to give users the ability to write code and have it compiled for use, yes?
If so, you may want to look into the CSharpCodeProvider class, the RazorTemplateEngine class and the System.CodeCom.Compiler namespace.
Have a look here for some information on those classes:
CSharpCodeProvider: http://support.microsoft.com/kb/304655
RazorTemplateEngine: http://msdn.microsoft.com/en-us/library/system.web.razor.razortemplateengine(v=vs.111).aspx

Html Extension methods are not accessible in MVC Partial Pages

Hi I'm trying to create a declarative Html helper method inside MVC partial page, everything works fine. But when I want to use built-in Html extension methods, I see there are no extension methods. Also I checked my view's webconfig file to add System.Web.Mvc.Html namespace. everything is OK, but I don't know why it's not working.
Any advice will be helpful.
Edit : Here is my code :
#using WebVoter.ViewModel
#using System.Web.Mvc.Html
#helper GetVoteList(IList<VoteQuestionViewModel> voteQuestionList)
{
<div class="head-panel">
#*For example ActionLink is not accessible here*#
#Html.ActionLink(....);
</div>
}
Inside Razor helpers you do not have reference to HtmlHelper. You need to pass it as parameter from the calling code:
#helper GetVoteList(HtmlHelper html, IList<VoteQuestionViewModel> voteQuestionList)
{
<div class="head-panel">
#html.ActionLink(....)
</div>
}
and when you want to call this helper from some view you need to pass the reference:
#GetVoteList(Html, ...)
Personally I've always preferred writing extension methods to the HtmlHelper class instead of using those inline Razor helpers. They would then be used as standard helpers:
#Html.GetVoteList(...)
The advantage is that you are no longer tied to Razor. Extension methods are view engine agnostic and make transition to other view engines less painful because they are C# code. Another benefit is that they can be unit tested.
There is a web.config under Views folder. Can you modify this config too?

When is it appropriate to create HTML code in the controller (or back end)

I am doing some complex logic involving loads of recursion to create a (complex) piece of HTML.
I started off doing this in the View using functions in Razor because I felt as HTML it belonged there.
But as it started getting more complex I thought I would rather do it in back-end code. Which it currently is.
It still feels a bit smellish though, and I am wondering if I should move it to the View again (which obviously clutters the view)
Which technically is more correct? When is it appropriate to use back-end code to generate HTML?
Thanks for your input.
create more granular partial views and partial models to maintain MVC pattern.
when your correctly select a model for your view, even partial, generating HTML in view is not a problem. you may end up with 20 views and 5 more models, but controller will be just selecting views and populating models which is good.
Don't do it in controller. You can extend the HtmlHelper class and do the stuff there. For example if you are using a paging helper.
Create a static class HtmlHelpers
namespace YourMvcApplication.WebUI.HtmlHelpers
{
public static class PagingHelpers
{
public static MvcHtmlString PageLinks(this HtmlHelper html,int totalPages)
{
StringBuilder result = new StringBuilder();
// do the complex logic to create dynamic html and append to
// String Builder
return MvcHtmlString.Create(result.ToString());
}
}
}
Add reference to this class in all views in web.config.
<namespaces>
<add namespace="YourMvcApplication.WebUI.HtmlHelpers"/>
</namespaces>
Use and resuse this Html Helper methods wherever required.
<div>
#Html.PageLinks(Model.TotalPages)
</div>

Categories