ASP .NET MVC localization when dynamic variables are included? - asp.net-mvc

Consider the following string if I need to localize it:
You need to write <b>#ViewBag.CharacterAmount</b> characters to be able to
hand-in this homework. You're still missing <b id="charactersRemaining">
#ViewBag.CharacterAmount</b> characters to reach this limit.
What would be the best approach? Using string.Format is a bit complex, since ASP .NET MVC escapes HTML code, and besides, I'd rather be free of HTML code in my resource files. However, I still need to be able to refer to those values inside the b tags from JavaScript.
Any ideas? What is your approach to this when you do localization?

You could write a custom helper:
public static class ResourcesExtensions
{
public static IHtmlString Resource(this HtmlHelper htmlHelper, string message, params object[] args)
{
var parameters = args.Select(x => htmlHelper.Encode(x)).ToArray();
return new HtmlString(string.Format(message, parameters));
}
}
As you can see the HTML helper encodes only the values. We have full control over the rest of the message because it is in the resources file and we suppose that it is valid HTML, so no problem with XSS.
and then have a resources file to your project which will contain for example the following key:
MyMessage = You need to write <b>{0}</b> characters to be able to hand-in this homework. You're still missing <b id="charactersRemaining">{1}</b> characters to reach this limit.
and then don't forget to mark this resources file with the PublicResXFileCodeGenerator custom tool so that Visual Studio generates a public class that will allow you to access the properties in the view.
and finally in the view:
#Html.Resource(Resources.MyMessage, (int)ViewBag.CharacterAmount, (int)ViewBag.CharacterAmount)
The reason you need to cast is because extension method cannot dispatch dynamic arguments. But obviously that's not a problem at all because you shouldn't be using ViewBag/ViewData but you should be using view models and strongly typed view models so in your real code you will have:
#Html.Resource(Resources.MyMessage, Model.CharacterAmount, Model.CharacterAmount)
One downside with this approach is that we have moved some markup in the resources file which unfortunately might render the views a little less understandable and when we need to modify it, we should do this in all localized versions.
Another approach of course consists into putting in your resources file every distinct part of this markup and then:
#Resources.YouNeedToWrite <b>ViewBag.CharacterAmount</b> #Resources.StillMissing
<b id="charactersRemaining">ViewBag.CharacterAmount</b> #(Resources.ToReachLimit).

Related

ASP.NET MVC with Razor, custom HtmlHelper outputs just text

I am experimenting with custom HtmlHelpers, but can't get even a basic one to work correctly. My code (just for testing sake) looks like this -
My class -
namespace HtmlHelpers.Extensions
{
public static class Helpers
{
public static string MySubmitButton(this HtmlHelper helper)
{
return String.Format("<input type=\"submit\" value=\"Submit This\">");
}
}
}
In my view -
#using HtmlHelpers.Extensions;
#Html.MySubmitButton()
This I believe should generate a simple submit button, but instead, it justs writes the following text to the screen -
<input type="submit" value="Submit">
I inspected the element, and for some reason the entire input element is being surrounded with double quotes.
Anyone know why? Thanks!
I believe you should be returning a MvcHtmlString class. Try
public static MvcHtmlString MySubmitButton(this HtmlHelper helper)
{
return MvcHtmlString.Create("<input type=\"submit\" value=\"Submit This\">");
}
although there's probably better ways to do this using a TagBuilder if you look at examples online, or the MVC source code since it's open source, you can look at their html helpers(although they are pretty complicated due to the way they layer them).
To directly answer your question of "why" it was displaying that as if it were a string, is Razor tries to be safe and convert anything you display as text instead of HTML/script. For example, #Model.PeronName will escape any characters in the peron's name with HTML character codes. Consider if there was no protection like this, and one of your users changed their name to be <script>someDangerousJavascriptThatWouldChangeCurrentUsersPassword()</script>, then posted on your forum or anywhere their name would appear, then other users visit the page, and that javascript runs and POSTS a change password form for that current user's password to some password that the hacker chose in the script. There are a wide variety of complicated attacks like this, or users might accidentally enter angle brackets(and while fairly harmless if treated as HTML they will mess up your page display).
For that reason MVC will assume just about any string is not HTML and thus replace things like <script> with <script> which basically is a way of saying "this is not html/script, I want you to display the less-than symbol and greater than symbol". If you wanted to display something as HTML, there is a #Html.Raw() helper for that, and it won't clean output, and thus should never be used with a string that you concatenated together from any data that a user my supply.

How to handle "magic strings" in MVC views, e.g. element id:s?

In my MVC views I frequently need to reference various elements from JavaScript, and for that purpose I define the id attribute for those elements. This is often in conjunction with Ajax calls that update content of various container elements.
A short example (Razor):
<table id="PersonList">
...
</table>
<div id="PersonDetails">
<!-- This would be loaded dynamically using Ajax -->
<input type="hidden" name="CurrentPersonId" value="#Model.PersonId">
...
</div>
This contains three "magic strings": "PersonList", "PersonDetails" and "CurrentPersonId". An Ajax call might look like this:
$('#PersonDetails').load('#Url.Action("GetPersonDetails")', { PersonId: ... });
Again, the magic string "PersonDetails" appears.
Not good!
Can anyone suggest some "best practice" to define these magics string in a single place and use them in 1) the views, 2) preferably static JavaScript files that implement Ajax calls etc, and 3) CSS files?
I'm, thinking perhaps _Layout.cshtml could include an partial view that defines the magic strings for that controller or even for a specific action. It would examine what controller and/or action called it, and call the appropriate partial view based on that. I do something similar for .css and static .js already, letting me simply add Person.css and have that automatically included for all views for the Person controller.
The partial view would do something like this:
#{
const string PersonListId = "PersonList";
const string PersonDetailsId = "PersonDetails";
const string CurrentPersonIdName = "CurrentPersonId";
}
<script type="text/javascript">
NamesAndIds = {};
NamesAndIds.PersonListId = '#Html.Raw(PersonListId)';
NamesAndIds.PersonDetailsId = '#Html.Raw(PersonDetailsId)';
NamesAndIds.CurrentPersonIdName = '#Html.Raw(CurrentPersonIdName)';
</script>
This should let Razor code use the C# string consts to generate appropriate HTML, and static JavaScript files could reference NamesAndIds in jQuery selectors etc. (Assumes that the consts defined in the partial view will be available in the calling view, which I doubt (haven't checked it yet)... How to use them in .css files I don't know.
Any better suggestions? How do you handle this problem?
I hope someone can come up with something better, but this is at least something.
In the main (non-partial) view I have a section at the top that defines the ids and names I need to use in multiple places in C#, HTML and JavaScript:
#{
const string PersonListId = "PersonList";
const string PersonDetailsId = "PersonDetails";
const string CurrentPersonIdName = "CurrentPersonId";
}
At the bottom, I have a script section that assigns the strings to suitable namespace container objects:
<script type="text/javascript">
MyNamespace = {};
MyNamespace.Ids = {};
MyNamespace.Names = {};
MyNamespace.Ids.PersonList = '#Html.Raw(PersonListId)';
MyNamespace.Ids.PersonDetails = '#Html.Raw(PersonDetailsId)';
MyNamespace.Names.CurrentPersonId = '#Html.Raw(CurrentPersonIdName)';
</script>
In each partial view that introduces additional items that I need to reference by id or name, I add similar code to extend MyNamespace.Ids and MyNamespace.Names with the required strings.
Now I can use the C# string constants in Razor view code to generate markup with the right ids and names and I can write regular static JavaScript files that reference MyNamespace.Ids and MyNamespace.Names to find the right ids and names, e.g. in jQuery selectors.
I also added similar stuff for action URLs that my Ajax calls use, and put them in MyNamespace.Urls:
MyNamespace.Urls.GetPerson = '#Html.Raw(Url.Action("GetPerson"))';
It's not ideal but it's straightforward and solves the most pressing issue of magic strings scattered all over the place. It will not detect errors at compile time, but renaming items will require a single string to be renamed at a single place, and if I rename or misspell MyNamespace.Ids.Something it will at least generate a runtime JavaScript error that can be seen in a JS console or similar.
For actions and file names (js, css) use T4MVC.
T4MVC is a T4 template for ASP.NET MVC apps that creates strongly
typed helpers that eliminate the use of literal strings in many
places.
I wouldn't worry about ids and css class names.
Try defining custom routes
PeopleLists/{PeopleList}/Person/{PersonID}
Then your URL would look like this
http://www.mysite.com/PeopleLists/Friends/Person/Pete

Adapting a Custom Html Helper for Razor (it uses HtmlTextWriter so returns void)

The Problem
I have a very nifty menu Html helper written for WebFormViewEngine views. This engine allows your helpers to return void, and still be able to use:
#Html.Theseus
This is great for my helper, because it can then render the menu using HtmlTextWriter, that renders directly to the output stream.
In Razor views, however, the Html helpers are expected to return a value (usually MvcHtmlString) which is what gets added to the output. Small difference, big consequence.
There is a way around this, as pointed out to me by GvS (see ASP.NET MVC 2 to MVC 3: Custom Html Helpers in Razor) as follows:
If the helper returns void, then do the following:
#{Html.Theseus;}
(Essentially, you are just calling the method, rather than rendering into the view).
Whilst still neat, this is not quite the same as #Html.Theseus. So...
My code is complex but works very well, so am loath to go through major edits, ie, replacing the HtmlTextWriter with another writer. A snippet of the code goes like:
writer.AddAttribute(HtmlTextWriterAttribute.Href, n.Url);
writer.AddAttribute(HtmlTextWriterAttribute.Title, n.Description);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.WriteEncodedText(n.Title);
writer.RenderEndTag();
// Recursion, if any
// Snip off the recursion at this level if specified by depth
// Use a negative value for depth if you want to render the entire sitemap from the starting node
if ((currentDepth < depth) || (depth < 0))
{
if (hasChildNodes)
{
// Recursive building starts here
// Open new ul tag for the child nodes
// "<ul class='ChildNodesContainer {0} Level{1}'>";
writer.AddAttribute(HtmlTextWriterAttribute.Class, "Level" + currentDepth.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
// BuildMenuLevel calls itself here to
// recursively traverse the sitemap hierarchy,
// building the menu as I go.
// Note: this is where I increase the currentDepth variable!
BuildChildMenu(currentDepth + 1, depth, n, writer);
// Close ul tag for the child nodes
writer.RenderEndTag();
}
}
It wouldn't be fun to re write with TagBuilders. As it stands, it renders any type of menu, including the "Incremental Navigation" as described in my 4guysfromrolla article:
Implementing Incremental Navigation with ASP.NET
The Options:
I guess I could return an empty MvcHtmlString, but that is pretty much the definition of a hack...
The only alternative is to head off into the sunset and rewrite the helper using the TagBuilder to build each tag, add that to a StringBuilder, then build the next tag, etc, and then use the StringBuilder instance to create the MvcHtmlString. Seriously ugly, unless I could do something like...
The Question:
Is there a way to:
Stop the HtmlTextWriter rendering to the stream and instead use it like a StringBuilder that at the end of the process I use to create an MvcHtmlString (or HtmlString)?
Sounds unlikely, even as I write...
PS:
The great thing about the HtmlTextWriter is that you can build large quantities of tags, instead of building them one by one as with a TagBuilder.
Contrary to the responses you received for your other question Razor does not require that you return an HtmlString. The problem with your code right now is that you are writing directly to the response stream. Razor executes things inside-out which means that you can mess up the response order (see a similar question).
So in your case you could probably do this (though i haven't tested it):
public static void Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
}
Edit (follow up to address your comments):
Html Helpers are perfectly capable of either returning a HtmlString directly or void and writing to the context writer. For example, both Html.Partial and Html.RenderPartial work fine in Razor. I think what you are confusing is the syntax required to call one version and not the other.
For example, consider an Aspx view:
<%: Html.Partial("Name") %>
<% Html.RenderPartial("Name") %>
You call each method differently. If you flip things around, things will just not work. Similarly in Razor:
#Html.Partial("Name")
#{ Html.RenderPartial("Name"); }
Now it just so happens that the syntax to use a void helper is a lot more verbose in Razor compared to Aspx. However, both work just fine. Unless you meant something else by "the issue is with a html helper not being able to return void".
By the way, if you really want to call your helper using this syntax: #Html.Theseus() you could do this:
public static IHtmlString Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
return new HtmlString("");
}
But that's a bit of a hack.

How to alter the rendering of links in ASP.NET MVC?

Our designers have come up with button styles for an application which require the addition of <span> tags inside the <a> tags of our links.
In ASP.NET we implemented this by adding an App_Browsers entry for Link Buttons.
How would I go about doing this in ASP.NET MVC?
I've contemplated creating my own versions of all of the various HTML helper functions for creating ActionLinks and RouteLinks but this seems to be quite a 'brute force' way of doing things.
Is there a nice elegant way of doing it?
I know we could write some simple jQuery to do it, but we'd rather have the markup coming out of the server correctly in the first place.
Actually I think writing a new helper is exactly the way I would go. Seems to me that that's exactly what they are there for and it makes them very re-usable too.
You could always write one extension method, that takes another one (one of the built-in ones) as an argument, and wrappes the <span> around your link text before calling it. It should be quite easy to do with lambdas...
public static string SpanLink(this HtmlHelper helper,
string linkText, object args, Action<string> action)
where TController : IController
{
action("<span>" + linkText + "</span>", args);
}
And to call it:
<%= Html.SpanLink<HomeController>("link text", (s) => Html.ActionLink<HomeController>(c => c.Index(s));
(This code is typed directly into the answer field of SO - I haven't even checked it to make sure it compiles. So bear with me if it doesn't work on the first try...)

Converting classic ASP.NET custom control to MVC

Ok, this is for me a very tough challenge. We're taking our existing ASP.NET website and converting (redesigning the PL only) to MVC. Our site is very complex. But the hard part is to convert the existing custom controls to MVC equivilant. The custom controls (I am not talking about user controls) are just of course a class currently that inherits System.Web.UI.Control and uses that object throughout. For example, we have some properties at the top of this existing custom class like so:
Dictionary<int, Control> configControls;
DropDownList kControl;
CheckBox confirmBox;
These all are variables of type Web controls in classic ASP.NET.
So I figured maybe what I could do (without building entire new custom controls from scratch) is to use the HtmlHelper object. So I tried this:
(include first the using statement that includes System.Web.MVC.Html at the top of my new custom class in our new web project)
private HtmlHelper helper;
Dictionary configControls;
helper.DropDownList
but this is not working. I guess I can't use this object just like this ?? I figured I can use HtmlHelper in the Dictionary and then make variable types off of helper. but those are just extension methods, not objects
I don't know of an equivalent to something like the generic "Control" we had available to us to inherit from such as in classic ASP.NET. Surely it won't be the same in MVC obviusly (stateless and a completely diff way of doing things) but what can I use in MVC with the same concept sort of?
So I figured maybe what I could do (without building entire new custom controls from scratch) is to use the HtmlHelper object. So I tried this:
(include first the using statement that includes System.Web.MVC.Html at the top of my new custom class in our new web project)
private HtmlHelper helper;
Dictionary configControls;
helper.DropDownList
but this is not working. I don't even know if this approach will work in my custom control. And when I try to use my helper variable, I get no extension methods unless it's inside an existing extension method where the signature has an HtmlHelper param passed in. So when I create that private variable just in my custom class outside, I get nothing in intellisense to choose from when doing "helper.". So do I need to define that object like this: ?
private HtmlHelper htmlHelper = new HtmlHelper();
but it's asking for a ViewContext and an IViewDataContainer as params. If I'm building out a custom method that knows nothing yet about its view (it shouldn't need to) because I'm simply creating strings of HMTL in this custom class to be passed to the Extension method to ultimately spit out fields then maybe I can't use HtmlHelper this way in a custom class like this.
So can I use that object in a way instead of "Control"? Maybe I can even in my dictionary variable use type object in place of control ? I don't know and then cast object to type HtmlHelper when I need to use or reference that value from the dictionary? But for now, I figured I can use HtmlHelper object in the Dictionary and then make variable types off of helper. but those are just extension methods, not objects.
I hope I am making any sense here when you read this.
I just blogged about this last night, some of this might be helpful for you.
WebForms And MVC In Harmony — Almost…
Basically it discusses some options for emulating "WebControls" using MVC.
Additionally, you can still use WebControls like you could before (granted they may not work if they need things like the ViewState). The problem I've discovered with that is you have a disconnect from the inline render code and the WebControls themselves.
I did write this method last night which let you use WebControls with inline code.
using System.Reflection;
using System.IO;
using System.Web.UI;
using System.Web;
using System.Web.Mvc;
public static class MyExtensionMethods {
//example method - renders a webcontrol to the page
public static void RenderControl(this HtmlHelper helper, Control control) {
//perform databinding if needed
MethodInfo bind = control.GetType().GetMethod("DataBind");
if (bind is System.Reflection.MethodInfo) {
bind.Invoke(control, null);
}
//render the HTML for this control
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
control.RenderControl(html);
//write the output
HttpContext.Current.Response.Write(writer.ToString());
//and cleanup the writers
html.Dispose();
writer.Dispose();
}
}
//then used like...
<% int[] numbers = { 1, 2, 3, 4, 5 }; %>
<% this.Html.RenderControl(new DataGrid() { DataSource = numbers }); %>
Just an interesting concept you might be interested in.
Short of hacking webforms controls into your MVC application, servercontrols with many methods do not map to MVC.
They are replaced by partials and controllers(or subcontrollers if you like that sort of thing).
If all you want to do is render some HTML based on a few parameters, then a Helper is what you are after. Static Class, static methods. If however, you need to keep state, and do a bunch of stateful stuff, then a partial, JS, and controller(or subcontroller) are really what you are after.
Server Controls that manage their own state really are a thing of the past in MVC.
Remember that MVC is an attempt to use the web the way it was meant to work, particularly if you bring REST into the picture. Webforms is a fudge to make the web work like windows forms.
I would create needed business logic, shared partial view (probably, with quite a lot of well structured javascript lines attached) and seperated controller.
Then i would use this bunch of code through partial request technique.
Not sure how much this will be of help but, do have a look at this series of blog post
Custom controls everywhere
Also have a look at the Catharsis project
Web-Application Framework - Catharsis - Part I - New Solution
The codeplex URL for the same is
Catharsis
This project has some good examples of control creating for asp.net mvc.

Resources