I am creating a MVC-Project. Using MVC 4 and Razor. After building some pages I was wondering: what is the difference between
MvcHtmlString.Create()
and
Html.Raw()
Would be nice if you could help me here to understand that.
Thanks in advance!
This is an excellent opportunity to look at the source code that's available to us for ASP.NET (https://github.com/aspnet/AspNetWebStack/).
Looking at HtmlHelper.cs, this is the code for Html.Raw():
public IHtmlString Raw(string value)
{
return new HtmlString(value);
}
public IHtmlString Raw(object value)
{
return new HtmlString(value == null ? null : value.ToString());
}
And this is the code for the MvcHtmlString class:
namespace System.Web.Mvc
{
public sealed class MvcHtmlString : HtmlString
{
[SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "MvcHtmlString is immutable")]
public static readonly MvcHtmlString Empty = Create(String.Empty);
private readonly string _value;
public MvcHtmlString(string value)
: base(value ?? String.Empty)
{
_value = value ?? String.Empty;
}
public static MvcHtmlString Create(string value)
{
return new MvcHtmlString(value);
}
public static bool IsNullOrEmpty(MvcHtmlString value)
{
return (value == null || value._value.Length == 0);
}
}
}
The most significant difference is that Html.Raw() accepts any object, while MvcHtmlString.Create() only accepts strings.
Also, Html.Raw() returns an interface, while the Create method returns an MvcHtmlString object.
Lastly, the Create deals with null differently.
There is no practical difference.
The MvcHtmlString.Create creates an instance of MvcHtmlString, while the Html.Raw method creates an instance of HtmlString, but MvcHtmlString just inherits from HtmlString, so they work the same.
The other answers focus more on the technical differences, if there are any. I think however there is another aspect: They serve different use cases / are used in different situations.
Html.Raw(...) is a method of IHtmlHelper. These are intented for use in razor views. It can be used to render raw HTML strings 'as is', without them getting encoded.
Since rendering user generated HTML content can be a security risk, it is very important to know when a string can contain HTML code, and for it to be sanitized. One of the main sources of security problems with old languages like ASP and PHP is rendering strings un-encoded per default, so you can see why, per default, ASP.NET MVC renders strings encoded. You want the (few) cases where your program renders a raw HTML string to be 'opt-in' and clear to see.
To better indicate these cases, it is good practice to store the HTML strings in a dedicated data type, like HtmlString. These objects will be rendered un-encoded, so you don't need Html.Raw. For this you can use MvcHtmlString.Create(...), or, more simply, new HtmlString(...), even if you don't have access to an IHtmlHelper (for example in a view model).
To illustrate this, consider this example of a view model for an ASP.NET MVC view with a title that does not contain HTML, and a content that does:
class MyViewModel
{
public string Title { get; set; }
public HtmlString SomeHtmlContent { get; set; }
}
This can be rendered on the page like this - notice that you don't need Html.Raw to render the HTML content:
<div>
<h1>#Model.Title</h1>
<div>
#Model.SomeHtmlContent
</div>
<div>
Related
New to MVC and have been running through the tutorials on the asp.net website.
They include an example of a custom html helper to truncate long text displayed in a table.
Just wondering what other solutions people have come up with using HTML helpers and if there are any best practices or things to avoid when creating/using them.
As an example, I was considering writing a custom helper to format dates that I need to display in various places, but am now concerned that there may be a more elegant solution(I.E. DataAnnotations in my models)
Any thoughts?
EDIT:
Another potential use I just thought of...String concatenation.
A custom helper could take in a userID as input and return a Users full name...
The result could be some form of (Title) (First) (Middle) (Last) depending on which of those fields are available. Just a thought, I have not tried anything like this yet.
I use HtmlHelpers all the time, most commonly to encapsulate the generation of boilerplate HTML, in case I change my mind. I've had such helpers as:
Html.BodyId(): generates a conventional body id tag for referencing when adding custom css for a view.
Html.SubmitButton(string): generates either an input[type=submit] or button[type=submit] element, depending on how I want to style the buttons.
Html.Pager(IPagedList): For generating paging controls from a paged list model.
etc....
One of my favorite uses for HtmlHelpers is to DRY up common form markup. Usually, I have a container div for a form line, one div for the label, and one label for the input, validation messages, hint text, etc. Ultimately, this could end up being a lot of boilerplate html tags. An example of how I have handled this follows:
public static MvcHtmlString FormLineDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> selectList, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
return FormLine(
helper.LabelFor(expression, labelText).ToString() +
helper.HelpTextFor(expression, customHelpText),
helper.DropDownListFor(expression, selectList, htmlAttributes).ToString() +
helper.ValidationMessageFor(expression));
}
public static MvcHtmlString FormLineEditorFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string templateName = null, string labelText = null, string customHelpText = null, object htmlAttributes = null)
{
return FormLine(
helper.LabelFor(expression, labelText).ToString() +
helper.HelpTextFor(expression, customHelpText),
helper.EditorFor(expression, templateName, htmlAttributes).ToString() +
helper.ValidationMessageFor(expression));
}
private static MvcHtmlString FormLine(string labelContent, string fieldContent, object htmlAttributes = null)
{
var editorLabel = new TagBuilder("div");
editorLabel.AddCssClass("editor-label");
editorLabel.InnerHtml += labelContent;
var editorField = new TagBuilder("div");
editorField.AddCssClass("editor-field");
editorField.InnerHtml += fieldContent;
var container = new TagBuilder("div");
if (htmlAttributes != null)
container.MergeAttributes(new RouteValueDictionary(htmlAttributes));
container.AddCssClass("form-line");
container.InnerHtml += editorLabel;
container.InnerHtml += editorField;
return MvcHtmlString.Create(container.ToString());
}
public static MvcHtmlString HelpTextFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string customText = null)
{
// Can do all sorts of things here -- eg: reflect over attributes and add hints, etc...
}
Once you've done this, though, you can output form lines like this:
<%: Html.FormLineEditorFor(model => model.Property1) %>
<%: Html.FormLineEditorFor(model => model.Property2) %>
<%: Html.FormLineEditorFor(model => model.Property3) %>
... and BAM, all your labels, inputs, hints, and validation messages are on your page. Again, you can use attributes on your models and reflect over them to get really smart and DRY. And of course this would be a waste of time if you can't standardize on your form design. However, for simple cases, where css can supply all the customization you need, it works grrrrrrrrreat!
Moral of the story -- HtmlHelpers can insulate you from global design changes wrecking hand crafted markup in view after view. I like them. But you can go overboard, and sometimes partial views are better than coded helpers. A general rule of thumb I use for deciding between helper vs. partial view: If the chunk of HTML requires a lot of conditional logic or coding trickery, I use a helper (put code where code should be); if not, if I am just outputting common markup without much logic, I use a partial view (put markup where markup should be).
Hope this gives you some ideas!
Well in the case of formatting the DisplayFormat attribute could be a nice solution:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Date { get; set; }
and then simply:
#Html.DisplayFor(x => x.Date)
As far as truncating string is concerned a custom HTML helper is a good solution.
UPDATE:
Concerning your EDIT, a custom HTML helper might work in this situation but there's also an alternative approach which I like very much: view models. So if in this particular view you are always going to show the concatenation of the names then you could define a view model:
public class PersonViewModel
{
public string FullName { get; set; }
}
Now the controller is going to query the repository to fetch the model and then map this model to a view model which will be passed to the view so that the view could simply #Html.DisplayFor(x => x.FullName). The mapping between models and view models could be simplified with frameworks like AutoMapper.
public static HtmlString OwnControlName<T, U>(this HtmlHelper<T> helper, Expression<Func<T, U>> expression, string label_Name = "", string label_Title = "", Attr attr = null)
{
TemplateBuilder tb = null;
string template = null;
if (expression == null) throw new ArgumentException("expression");
obj = helper.ViewData.Model;
tb.Build(obj, expression.Body as MemberExpression, typeof(T), new SimpleTemplate(new TextArea()), label_Name, label_Title, attr);
template = tb.Get();
return new MvcHtmlString(template);
}
Coming from the asp.net background, I really appreciated the concept of 'validationGroup' when adding validation to a page. I've been searching for a corresponding concept within mvc.net and haven't had much luck.
Is this concept available in mvc.net? If not, what alternatives do I have?
Unfortunately no, it doesn't come with anything like that.
I blogged about a workaround a wee while back.
ASP.NET MVC - Validation Summary with 2 Forms & 1 View
The jist of the blog post:
namespace System.Web.Mvc
{
public static class HtmlExtensions
{
public static string ActionValidationSummary(this HtmlHelper html, string action)
{
string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
if (currentAction.ToLower() == action.ToLower())
return html.ValidationSummary();
return string.Empty;
}
}
}
And
<h2>Register</h2>
<%= Html.ActionValidationSummary("Register") %>
<form method="post" id="register-form" action="<%= Html.AttributeEncode(Url.Action("Register")) %>">
... blah ...
</form>
<h2>User Login</h2>
<%= Html.ActionValidationSummary("LogIn") %>
<form method="post" id="login-form" action="<%= Html.AttributeEncode(Url.Action("LogIn")) %>">
... blah ...
</form>
HTHs,
Charles
Expanding on Charlino's answer, and including HtmlAttributes and other ValidationSummary properties:
public static MvcHtmlString ActionValidationSummary(this HtmlHelper html, string action, bool excludePropertyErrors, string message, object htmlAttributes = null)
{
var currentAction = html.ViewContext.RouteData.Values["action"].ToString();
if (currentAction.ToLower() == action.ToLower())
{
return html.ValidationSummary(excludePropertyErrors, message, htmlAttributes);
}
return new MvcHtmlString(string.Empty);
}
Charles's method was the only approach I could find that actually worked for my purposes!
(I.e. two forms on one MVC page -> without doing forms inside partials and ajax loads for the partials. This was no good for me, as I wanted to return differing result sets to be rendered outside the form div, depending on which form was submitted)
I would advise a slight modification to the Html Extension though, because you still want a validation summary to be rendered for the non-matched validation summary so that client side validation works:
namespace System.Web.Mvc
{
public static class HtmlExtensions
{
public static MvcHtmlString ActionValidationSummary(this HtmlHelper html, string action)
{
string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
if (currentAction.ToLower() == action.ToLower())
return html.ValidationSummary();
return new MvcHtmlString("<div class=\"validation-summary-valid\" data-valmsg-summary=\"true\"><ul><li style=\"display:none\"></li></ul></div>");
}
}
}
Normally in an ASP.NET view one could use the following function to obtain a URL (not an <a>):
Url.Action("Action", "Controller");
However, I cannot find how to do it from a custom HTML helper. I have
public class MyCustomHelper
{
public static string ExtensionMethod(this HtmlHelper helper)
{
}
}
The helper variable has the Action and GenerateLink methods, but they generate <a>’s. I did some digging in the ASP.NET MVC source code, but I could not find a straightforward way.
The problem is that the Url above is a member of the view class and for its instantiation it needs some contexts and route maps (which I don’t want to be dealing with and I’m not supposed to anyway). Alternatively, the instance of the HtmlHelper class has also some context which I assume is either supper of subset of the context information of the Url instance (but again I don’t want to dealing with it).
In summary, I think it is possible but since all ways I could see, involve some manipulation with some more or less internal ASP.NET stuff, I wonder whether there is a better way.
Edit: For instance, one possibility I see would be:
public class MyCustomHelper
{
public static string ExtensionMethod(this HtmlHelper helper)
{
UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
urlHelper.Action("Action", "Controller");
}
}
But it does not seem right. I don't want to be dealing with instances of UrlHelper myself. There must be an easier way.
You can create url helper like this inside html helper extension method:
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var url = urlHelper.Action("Home", "Index")
You can also get links using UrlHelper public and static class:
UrlHelper.GenerateUrl(null, actionName, controllerName, null, null, null, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true)
In this example you don't have to create new UrlHelper class what could be a little advantage.
Here is my tiny extenstion method for getting UrlHelper of a HtmlHelper instance :
public static partial class UrlHelperExtensions
{
/// <summary>
/// Gets UrlHelper for the HtmlHelper.
/// </summary>
/// <param name="htmlHelper">The HTML helper.</param>
/// <returns></returns>
public static UrlHelper UrlHelper(this HtmlHelper htmlHelper)
{
if (htmlHelper.ViewContext.Controller is Controller)
return ((Controller)htmlHelper.ViewContext.Controller).Url;
const string itemKey = "HtmlHelper_UrlHelper";
if (htmlHelper.ViewContext.HttpContext.Items[itemKey] == null)
htmlHelper.ViewContext.HttpContext.Items[itemKey] = new UrlHelper(htmlHelper.ViewContext.RequestContext, htmlHelper.RouteCollection);
return (UrlHelper)htmlHelper.ViewContext.HttpContext.Items[itemKey];
}
}
Use it as:
public static MvcHtmlString RenderManagePrintLink(this HtmlHelper helper, )
{
var url = htmlHelper.UrlHelper().RouteUrl('routeName');
//...
}
(I'm posting this ans for reference only)
I wish to display content depending on the given role(s) of the active user , in the ASP.NET MVC.
Compare the old fashion way, using WebForms:
protected void Page_Load(Object sender, EventArgs e)
{
if(User.IsInRole("Administrator")) {
adminLink.Visible = true;
}
}
Now how would I go on writing that when using the ASP.NET MVC ?
From my point of view, it would be wrong to place it directly in the View File, and assigning a variable for every single view won't be pretty either.
Create Html helper and check current user roles in its code:
public static class Html
{
public static string Admin(this HtmlHelper html)
{
var user = html.ViewContext.HttpContext.User;
if (!user.IsInRole("Administrator")) {
// display nothing
return String.Empty;
// or maybe another link ?
}
var a = new TagBuilder("a");
a["href"] = "#";
a.SetInnerText("Admin");
var div = new TagBuilder("div") {
InnerHtml = a.ToString(TagRenderMode.Normal);
}
return div.ToString(TagRenderMode.Normal);
}
}
UPDATED:
Or create wrapper for stock Html helper. Example for ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName):
public static class Html
{
public static string RoleActionLink(this HtmlHelper html, string role, string linkText, string actionName, string controllerName)
{
return html.ViewContext.HttpContext.User.IsInRole(role)
? html.ActionLink(linkText, actionName, controllerName)
: String.Empty;
}
}
No you would be placing it in the view file, like so actually:
<% If (User.IsInRole("Administrator")) { %>
<div>Admin text</div>
<% } %>
this worked for me:
<% MembershipUser mu = Membership.GetUser();
if (mu != null)
if (Roles.IsUserInRole(mu.UserName, "Administrator"))
{
%>
<li class="paddingleftThree"><%= Html.ActionLink("User Administration", "GetUsers", "Account")%></li> <%} %>
The separation of concerns approach suggested in ASP.NET MVC 4 How do you serve different HTML based on Role? in my opinion is a better way to go.
Personally I avoid IsInRole check as much as possible in the code and leave it to declarative means to achieve role based restriction as much as possible. This ensures code remains maintainable over time. I am not sure if this is a right or the wrong approach, but has worked well for me.
I am writing my own HtmlHelper extenstion for ASP.NET MVC:
public static string CreateDialogLink (this HtmlHelper htmlHelper, string linkText,
string contentPath)
{
// fix up content path if the user supplied a path beginning with '~'
contentPath = Url.Content(contentPath); // doesn't work (see below for why)
// create the link and return it
// .....
};
Where I am having trouble is tryin to access UrlHelper from within my HtmlHelper's definition. The problem is that the way you normally access HtmlHelper (via Html.MethodName(...) ) is via a property on the View. This isn't available to me obviously from with my own extension class.
This is the actual MVC source code for ViewMasterPage (as of Beta) - which defines Html and Url.
public class ViewMasterPage : MasterPage
{
public ViewMasterPage();
public AjaxHelper Ajax { get; }
public HtmlHelper Html { get; }
public object Model { get; }
public TempDataDictionary TempData { get; }
public UrlHelper Url { get; }
public ViewContext ViewContext { get; }
public ViewDataDictionary ViewData { get; }
public HtmlTextWriter Writer { get; }
}
I want to be able to access these properties inside an HtmlHelper.
The best I've come up with is this (insert at beginning of CreateDialogLink method)
HtmlHelper Html = new HtmlHelper(htmlHelper.ViewContext, htmlHelper.ViewDataContainer);
UrlHelper Url = new UrlHelper(htmlHelper.ViewContext.RequestContext);
Am I missing some other way to access the existing HtmlHelper and UrlHelper instances - or do i really need to create a new one? I'm sure there isn't much overhead but I'd prefer to use the preexisting ones if I can.
Before asking this question I had looked at some of the MVC source code, but evidently I missed this, which is how they do it for the Image helper.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")]
public static string Image(this HtmlHelper helper, string imageRelativeUrl, string alt, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(imageRelativeUrl)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageRelativeUrl");
}
UrlHelper url = new UrlHelper(helper.ViewContext);
string imageUrl = url.Content(imageRelativeUrl);
return Image(imageUrl, alt, htmlAttributes).ToString(TagRenderMode.SelfClosing);
}
Looks like instantiating a new UrlHelper is the correct approach after all. Thats good enough for me.
Update: RTM code from ASP.NET MVC v1.0 Source Code is slightly different as pointed out in the comments.
File: MVC\src\MvcFutures\Mvc\ImageExtensions.cs
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")]
public static string Image(this HtmlHelper helper, string imageRelativeUrl, string alt, IDictionary<string, object> htmlAttributes) {
if (String.IsNullOrEmpty(imageRelativeUrl)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "imageRelativeUrl");
}
UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
string imageUrl = url.Content(imageRelativeUrl);
return Image(imageUrl, alt, htmlAttributes).ToString(TagRenderMode.SelfClosing);
}
I faced a similar issue and decided that it would be easier to just call the UrlHelper in the view and pass the output to my HtmlHelper extension. In your case it would look like:
<%= Html.CreateDialogLink( "text", Url.Content( "~/...path.to.content" ) ) %>
If you want to access the extension methods on the existing HtmlHelper that is passed into your class, you should only need to import System.Web.Mvc.Html in your source code file and you will get access to them (that's where the extension classes are defined). If you want a UrlHelper, you'll need to instantiate that as the HtmlHelper you are getting doesn't have a handle for the ViewPage that it's coming from.
If you need to create a UrlHelper in a utility class you can do the following :
string url = "~/content/images/foo.jpg";
var urlHelper = new UrlHelper(new RequestContext(
new HttpContextWrapper(HttpContext.Current),
new RouteData()), RouteTable.Routes);
string absoluteUrl = urlHelper.Content(url);
This allows you to use routing or '~ expansion' away from an MVC context.
Well, you can always pass the instance of the page to the extension method. I think that is a much better way of doing this than creating new instances in your method.
You could also define this method on a class that derives from MasterPage/ViewMasterPage and then derive the page from that. This way, you have access to all the properties of the instance and don't have to pass them around.