Maintaining current indentation in a custom MVC HTML-extension helper - asp.net-mvc

Let's say I have the following custom extension to System.Web.Mvc.HtmlHelper<T>:
public static MvcHtmlString WrapInDiv<TModel, TValue>(this HtmlHelper<TModel> html, String text)
{
String htmlRaw = String.Format("<div>{0}</div>", text);
var xDoc = XDocument.Parse(htmlRaw); // For formatting
return new MvcHtmlString(xDoc.ToString());
{
..and I use it in this view snippet:
<span>
#Html.WrapInDiv("sasuage")
</span>
The result would be as follows:
<span>
<div>
Sausage
</div>
</span>
because the XDocument writer obviously don't know that we're already in a section with four characters indentation. Any way I can fix this so that my HTML helpers print perfectly indented multi-line HTML?

Why do you want to keep the indentation proper? Browsers do not care, and it's a lot of work and a very hard problem to resolve, especially when you are doing helpers and do not have the whole context.
The best way, if you really, really need visually formatted outputs, is to do it globally for each page generated with an HTTP Module that intercept answers and maybe one of the .Net port of Html Tidy.
public class HtmlTidyModule: IHttpModule
{
private EventHandler ReleaseRequestStateEvent = null;
public void Dispose()
{
if (ReleaseRequestStateEvent != null)
context.ReleaseRequestState -= ReleaseRequestStateEvent;
}
public void Init(HttpApplication context)
{
ReleaseRequestStateEvent = new EventHandler(event_ReleaseRequestState);
context.ReleaseRequestState += ReleaseRequestStateEvent;
}
void event_ReleaseRequestState(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
// You need to implement HtmlTidyFilter to handle the response
app.Response.Filter = new HtmlTidyFilter(app.Response.Filter)
}
}
But you must ask yourself, do you really want to do this?? Most integrated HTML viewers/inspectors in browsers does this already (F12 in chrome/FF I believe).

Related

Creating a HtmlHelper to display content if certain conditions are fulfilled

I want to create a simple HtmlHelper to that I can use like this:
#using(Html.DisplayIf(Object object))
{
...
}
I tried the method suggested here, but unlike the guy who asked that question I would like the content between the brackets not to be rendered at all, not just hidden.
Is there a way to prevent the textwriter from writing the content between the brackets, or some other method that would be appropriate to solve my problem?
Edited
You can use the method described here: Capture wrapped content in BeginForm style disposable html helper.
I've applied the first method to your example.
public static class HtmlExtensions
{
public static HelperResult DisplayIf(this HtmlHelper html, Func<object, HelperResult> template, bool show)
{
return new HelperResult(writer =>
{
if (show)
{
template(null).WriteTo(writer);
}
});
}
}
You can call it like this:
#* Will render *#
#Html.DisplayIf(
#<span>test1</span>, true
)
#* Will not render *#
#Html.DisplayIf(
#<span>test2</span>, false
)

MVC 5 custom HtmlHelper for lightweight content editing

After years of working with WebForms I recently started the transition to MVC. I'm trying to create a plugable, lightweight content editing module but I've run into some problems.
The idea is simple: create a HtmlHelper named EditableSimpleHtml that can be used in a #using... { } syntax so that the following can be achieved in a razor view:
#using (Html.EditableSimpleHtml("MyKey"))
{
<h3>Test</h3>
<p>
1<br />
</p>
}
The value between the {...} is the default value for when no content can not be found in the data storage.
I've create a HtmlHelper. Below is a simplified version:
public static IDisposable EditableSimpleHtml(this HtmlHelper helper, string key)
{
// Get the content from the data storage using the key (I will not show the provider itself, its just a provider that will access a db)
var provider = ContentEditing.Provider;
string value = provider.GetValue(key);
if (value == null)
{
// No value found in the data storage for the supplied key, we have to use the default value from within the #using... { } statement
// Can I get that value here? I want to to store it initialy in the data storage
value = "..."; // How to get html from within the #using... { }?
}
return new ContentEditableHtmlString(helper, value);
}
public class ContentEditableHtmlString : IDisposable
{
private readonly HtmlHelper _helper;
public ContentEditableHtmlString(HtmlHelper helper, string value)
{
_helper = helper;
var builder = new TagBuilder("div");
var writer = _helper.ViewContext.Writer;
writer.Write(builder.ToString(TagRenderMode.StartTag));
writer.Write(value);
}
public void Dispose()
{
_helper.ViewContext.Writer.Write("</div>");
}
}
The problem is that I can't get the (default) content from within the #using... { } statement in the HtmlHelper, or at least I don't know how. I need it in case I want to store it to the database initially.
Second problem is that the value between the #using... { } statement will always be rendered. In the case when the content can be loaded from the data storage I want the default value to be replaced with the value from the data storage.
Is there a way to achieve this or did I start of on a completely wrong path?
You can not get the html within the #using{...} statement the way you are doing right now.
The closest thing you can do is use Templated Razor Delegates
public static HelperResult EditableSimpleHtml(this HtmlHelper helper, string key,
Func<string, HelperResult> template)
{
var templateResult = template(null);
//you have your value here that you can return directly
//or you can return HelperResult to write to the response directly
var templateResultHtml = templateResult.ToHtmlString();
return new HelperResult(writer =>
{
templateResult.WriteTo(writer);
});
}
And in your view:
#Html.EditableSimpleHtml("MyKey", #<text>
<h3>Test</h3>
<p>#DateTime.Now</p>
</text>)

Difference between MvcHtmlString.Create() and Html.Raw()

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>

How to I replace URLs in rendered HTML using an ASP.NET MVC ActionFilter

I'm trying to create an ActionFilter to replace some text in my HTML. Basically when the server is using SSL I want to replace references to my CDN (http://cdn.example.com) with references directly to my server (https://www.example.com). So the structure is something like this (I assume OnResultExecuted is where I should start):
public class CdnSslAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if(filterContext.HttpContext.Request.IsSecureConnection)
{
// when the connection is secure,
// somehow replace all instances of http://cdn.example.com
// with https://www.example.com
}
}
}
This would be used in my secure controllers:
[CdnSsl]
public class SecureController : Controller
{
}
The reason I want to do this is my CDN doesn't support SSL. And there are references in the Master pages to CDN resources. Example:
<link href="http://cdn.example.com/Content/base.css" rel="stylesheet" type="text/css" />
I ended up using a variation on this blog post:
http://arranmaclean.wordpress.com/2010/08/10/minify-html-with-net-mvc-actionfilter/
with my own filter:
public class CdnSslAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.HttpContext.Request.IsSecureConnection)
{
var response = filterContext.HttpContext.Response;
response.Filter = new CdnSslFilter(response.Filter);
}
}
}
Then the filter looks like this (some code omitted for brevity):
public class CdnSslFilter : Stream
{
private Stream _shrink;
private Func<string, string> _filter;
public CdnSslFilter(Stream shrink)
{
_shrink = shrink;
_filter = s => Regex.Replace(s,#"http://cdn\.","https://www.", RegexOptions.IgnoreCase);
}
//overridden functions omitted for clarity. See above blog post.
public override void Write(byte[] buffer, int offset, int count)
{
// capture the data and convert to string
byte[] data = new byte[count];
Buffer.BlockCopy(buffer, offset, data, 0, count);
string s = Encoding.Default.GetString(buffer);
// filter the string
s = _filter(s);
// write the data to stream
byte[] outdata = Encoding.Default.GetBytes(s);
_shrink.Write(outdata, 0, outdata.GetLength(0));
}
}
I don't know but the answer from #Haacked at this question could help.
Performing replacements on the generated output inside of an action filter would be a bit complicated.
An easier approach (if you can edit your master pages) would be to write a new Html helper method (similar to the Html.Content() helper) that would conditionally emit the correct url. If you wanted that replacement to happen only for certain controllers then you could still have an action filter, but all it would do would be to set some flag in Request.Items and your helper could check that flag.
My suggestion is to follow #marcind's approach, one possibility is to use a custom extension method to generate the correct url depending on the current url scheme.
public static MvcHtmlString CdnActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName)
{
if(helper.ViewContext.HttpContext.Request.IsSecureConnection)
{
return helper.ActionLink(linkText, actionName, controllerName, "https", "www.yourhost.com"...);
}
return helper.ActionLink(linkText, actionName, controllerName);
}
One drawback of this approach is that you'll need to replace all your current ActionLink invocations in your views (or at least the ones you need) with an invocation to this extension method.

Role-Based Content asp.net mvc

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.

Resources