Generate URL in HTML helper - asp.net-mvc

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)

Related

Getting ModelState or ViewData from UrlHelper

I think the title is clear. I need to capture ModelState or ViewData inside an extension method for UrlHelper class. How can I do this?
I think the title is clear. I need to capture ModelState or ViewData
inside an extension method for UrlHelper class
You can't. An UrlHelper doesn't have this information. For example you have an UrlHelper instance in your controller where it's too early to talk about any ViewData. Use an extension for HtmlHelper instead:
public static void SomeExtension(this HtmlHelper html)
{
var viewData = html.ViewData;
}
and if you need to get an UrlHelper inside an extension method for HtmlHelper:
public static void SomeExtension(this HtmlHelper html)
{
UrlHelper url = new UrlHelper(html.ViewContext.RequestContext);
}

Instantiate a helper class but not in view

I need a way to instantiate a class thats supposed to help me with some querystring-building when it comes to my links in the view, and I cant use this methods that i require as static methods since that would cause the querystringbuilder to keep data during the whole life-time of the application (which would cause some serious problems)
So my question to you guys is,
Is it possible to some how be able to instantiate the class/object that i require but not in the actual view itself?
SO to keep the question simple.. is there anyway that I could do something like:
#MyInstantiatedObject.DoStuff()
in my view with out doing this before in my view:
#{
var MyInstantiatedObject = new MyClass()
}
I do get that I some where some how will need to instansiate the object, but my question is if its possible to do it in some other manner (like telling the web.config to handel it..or using some app_code #helper magic or something)
Thanks in advance!
What you are trying to achieve goes against the philosophy of MVC. If you want to keep the query string data between the actions, you can create your custom ActionLink html helper like this:
public static MvcHtmlString ActionLinkWithQueryString(this HtmlHelper htmlHelper,
string linkText, string actionName)
{
var routeValueDictionary = new RouteValueDictionary();
return htmlHelper.ActionLinkWithQueryString(linkText,
actionName, routeValueDictionary);
}
public static MvcHtmlString ActionLinkWithQueryString(this HtmlHelper htmlHelper,
string linkText, string actionName, RouteValueDictionary routeValues)
{
var queryString = HttpContext.Current.Request.QueryString;
if (queryString.Count > 0)
{
foreach (string key in queryString.Keys)
{
routeValues.Add(key, queryString[key]);
}
}
return htmlHelper.ActionLink(linkText, actionName, routeValues);
}
You can also create a custom RedirectToAction method in your Controller or in a Controller Extention like this:
private RedirectToRouteResult RedirectToActionWithQueryString(string actionName)
{
var queryString = Request.QueryString;
var routeValues = new RouteValueDictionary();
foreach (string key in queryString.Keys)
{
routeValues.Add(key, queryString[key]);
}
return RedirectToAction(actionName, routeValues);
}

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>

RouteLink in HtmlHelper?

How can I make up a RouteLink in a custom HtmlHelper? I know how to make it in a partial view but I want to build up a new link in a custom htmlhelper extension method with the use of a RouteLink. How to accomplish this?
Update: I noticed HtmlHelper.GenerateRouteLink. But what do I need to put in as parameters?
Here's an example. Let's suppose that you want to wrap the links into a div tag with some given class so that your resulting html looks like this:
<div class="foo">Some text</div>
You could write the following extension method:
public static class HtmlExtensions
{
public static MvcHtmlString CustomRouteLink(
this HtmlHelper htmlHelper,
string className,
string linkText,
object routeValues
)
{
var div = new TagBuilder("div");
div.MergeAttribute("class", className);
div.InnerHtml = htmlHelper.RouteLink(linkText, routeValues).ToHtmlString();
return MvcHtmlString.Create(div.ToString());
}
}
which could be used like this:
<%= Html.CustomRouteLink("foo", "Some text",
new { action = "index", controller = "home" }) %>
and this will produce the desired markup. Any other overloads of RouteLink could be used if necessary.
Once you get an instance of the UrlHelper you should be able to do whatever you want to do in your HtmlHelper method
UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);

How do I access HtmlHelper methods from within MY OWN HtmlHelper?

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.

Resources