How do I access query string parameters in asp.net mvc? - asp.net-mvc

I want to have different sorting and filtering applied on my view
I figured that I'll be passing sorting and filtering params through query string:
#Html.ActionLink("Name", "Index", new { SortBy= "Name"})
This simple construction allows me to sort. View comes back with this in query string:
?SortBy=Name
Now I want to add filtering and i want my query string to end up with
?SortBy=Name&Filter=Something
How can I add another parameter to list of already existing ones in ActionLink? for Example:
user requests /Index/
view has
#Html.ActionLink("Name", "Index", new { SortBy= "Name"})
and
#Html.ActionLink("Name", "Index", new { FilterBy= "Name"})
Links: The first one looks like /Index/?SortBy=Name and The second is /Index/?FilterBy=Name
I want when user pressed sorting link after he applied some filtering - filtering is not lost, so i need a way to combine my params.
My guess is there should be a way to not parse query string, but get collection of parameters from some MVC object.

so far the best way I figured out is to create a copy of ViewContext.RouteData.Values
and inject QueryString values into it.
and then modify it before every ActionLink usage.
still trying to figure out how to use .Union() instead of modifying a dictionary all the time.
<% RouteValueDictionary tRVD = new RouteValueDictionary(ViewContext.RouteData.Values); %>
<% foreach (string key in Request.QueryString.Keys )
{
tRVD[key]=Request.QueryString[key].ToString();
} %>
<%tRVD["SortBy"] = "Name"; %>
<%= Html.ActionLink("Name", "Index", tRVD)%>

My solution is similar to qwerty1000's. I created an extension method, ActionQueryLink, that takes in the same basic parameters as the standard ActionLink. It loops through Request.QueryString and adds any parameters found to the RouteValues dictionary that are not already present (so we can overwrite the original query string if needed).
To preserve the existing string but not add any keys the usage would be:
<%= Html.ActionQueryLink("Click Me!","SomeAction") %>
To preserve the existing string and add new keys the user would be:
<%= Html.ActionQueryLink("Click Me!","SomeAction", new{Param1="value1", Param2="value2"} %>
The code below is for the two usages, but it should be pretty easy to add other overloads to match the other ActionLink extensions as needed.
public static string ActionQueryLink(this HtmlHelper htmlHelper,
string linkText, string action)
{
return ActionQueryLink(htmlHelper, linkText, action, null);
}
public static string ActionQueryLink(this HtmlHelper htmlHelper,
string linkText, string action, object routeValues)
{
var queryString =
htmlHelper.ViewContext.HttpContext.Request.QueryString;
var newRoute = routeValues == null
? htmlHelper.ViewContext.RouteData.Values
: new RouteValueDictionary(routeValues);
foreach (string key in queryString.Keys)
{
if (!newRoute.ContainsKey(key))
newRoute.Add(key, queryString[key]);
}
return HtmlHelper.GenerateLink(htmlHelper.ViewContext.RequestContext,
htmlHelper.RouteCollection, linkText, null /* routeName */,
action, null, newRoute, null);
}

<%= Html.ActionLink("Name", "Index", new { SortBy= "Name", Filter="Something"}) %>
To preserve the querystring you can:
<%= Html.ActionLink("Name", "Index",
String.IsNullOrEmpty(Request.QueryString["SortBy"]) ?
new { Filter = "Something" } :
new { SortBy=Request.QueryString["SortBy"], Filter="Something"}) %>
Or if you have more parameters, you could build the link manually by using taking Request.QueryString into account.

Use ActionLinkCombined instead of ActionLink
public static string ActionLinkCombined(this HtmlHelper htmlHelper, string linkText, string actionName,
object routeValues)
{
var dictionary = new RouteValueDictionary();
foreach (var pair in htmlHelper.ViewContext.Controller.ValueProvider)
dictionary[pair.Key] = pair.Value.AttemptedValue;
if (routeValues != null)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(routeValues))
{
object o = descriptor.GetValue(routeValues);
dictionary[descriptor.Name] = o;
}
}
return htmlHelper.ActionLink(linkText, actionName, dictionary);
}

MVC4
#Html.ActionLink("link text","action",new { #id = 5, #name = "textName", #abc = "abc" })
OR
#Html.ActionLink("link text", "action", "controller", new { #id = 5, #name = "textName", #abc = "abc" }, new { #class = "cssClass" })
querystring would be like:
yourDomainRout/action/5?name=textName&abc=abc
it would have class="cssClass"

Related

passing attributes to ActionLink

I have the following code for generate a tags:
<ul>
#foreach (var schedule in scheduleGroup)
{
<li>
#Html.ActionLink(string.Format("{0:HH\\:mm}", schedule.RecurrenceStart), "EventOverview", "BaseEvent", new { id = schedule.BaseEvent.OID, type = schedule.BaseEvent.XPObjectType}, new Dictionary<string, object>
{
{"session", schedule.SessionId},
{"hall",schedule.HallId},
{"client",schedule.BasePlace.PremieraClientId}
})
}
</ul>
But, html attributes shows not correctly in the a tag . This is generated markup:
20:15
Where is a error?
Thanks.
UPDATE:
I want the following:
<a client="1" hall="1" session="15" href="/BaseEvent/EventOverview?id=36&type=Film"> 20:15 </a>
Your code:
#Html.ActionLink(string.Format("{0:HH\\:mm}", schedule.RecurrenceStart), "EventOverview", "BaseEvent",
new { id = schedule.BaseEvent.OID, type = schedule.BaseEvent.XPObjectType},
new Dictionary<string, object>
{
{"session", schedule.SessionId},
{"hall",schedule.HallId},
{"client",schedule.BasePlace.PremieraClientId}
})
If your intention is to generate a link as #Tommy said that is 'session', 'hall', 'schedule' are as queryStirng parameter, then the code should be:
#Html.ActionLink(string.Format("{0:HH\\:mm}", "schedule.RecurrenceStart"), "EventOverview", "BaseEvent",
new { id = schedule.BaseEvent.OID, type = schedule.BaseEvent.XPObjectType,
session= schedule.SessionId, hall =schedule.HallId, client =schedule.BasePlace.PremieraClientId},
null)
Otherwise if you want the 'session', 'hall', 'schedule' as html attributes(
according to your given code) , there are two matching ActionLink method signatures:
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
Object routeValues,
Object htmlAttributes
)
And
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
RouteValueDictionary routeValues,
IDictionary<string, Object> htmlAttributes
)
You have to choose one of them. That is send both parameters 'routeValues' and 'htmlAttributes' as anonymous object (1st one) or as typed object, 'RouteValueDictionary' for 'routeValues' and 'IDictionary<string, Object>' for 'htmlAttributes'. Your given code matched the 1st one, that is why the 'htmlAttributes' of type IDictionary<string, Object> treat as Object and generating the incorrect tag.
Correct code should be:
#Html.ActionLink(linkText: string.Format("{0:HH\\:mm}", "schedule.RecurrenceStart"), actionName: "EventOverview", controllerName: "BaseEvent",
routeValues: new { id = schedule.BaseEvent.OID, type = schedule.BaseEvent.XPObjectType },
htmlAttributes: new
{
session = schedule.SessionId,
hall = schedule.HallId,
client = schedule.BasePlace.PremieraClientId
} )
Or
#Html.ActionLink(string.Format("{0:HH\\:mm}", "schedule.RecurrenceStart"), "EventOverview", "BaseEvent",
new RouteValueDictionary(new { id = schedule.BaseEvent.OID, type = schedule.BaseEvent.XPObjectType }),
new Dictionary<string, object>
{
{"session", schedule.SessionId},
{"hall", schedule.HallId},
{"client", schedule.BasePlace.PremieraClientId}
})

Dynamic named parameter in route values collection using Razor view

I use a helper method to create sections in my view:
#helper CreateFacetSection(WebViewPage page, string sectionName,
List<FacetValue> model)
{
var showMore = int.Parse(WebConfigurationManager.AppSettings["ShowMore"]);
<section id="#sectionName">
<h4>#sectionName</h4>
<ul>
#for (int i = 0; i < model.Count(); i++)
{
if (i >= showMore)
{
#:<li class="hide">
}
else
{
#:<li>
}
FacetValue fv = model.ElementAt(i);
#page.Html.ActionLinkWithQueryString(fv.Range, "Search",
new { sectionName = fv.Range }, null);
#:(#fv.Count)
#:</li >
}
</ul>
#if(model.Count > showMore)
{
<a href="#" class="show-more" data-section="#sectionName">
#Localization.ShowMore ▼</a>
}
</section>
}
Now let's say I have this custom #Html.ActionLink helper used in the helper above:
#Html.ActionLinkWithQueryString(fv.Range, "Search", new { sectionName = fv.Range });
Is there any way of passing a dynamic named parameter in the route value collection? In the case above I'd like to have a parameter sectionName dynamically named so that I'd get the correct value bound in the action method. It would vary according to the sectionName I'm currently passing as parameter to the helper method...
Right now I'm getting a link like this:
http://leniel-pc:8083/realty/search?sectionName=Volta%20Redonda
It should be:
http://leniel-pc:8083/realty/search?City=Volta%20Redonda
City or whatever I pass as parameter instead of sectionName because "City" is the value of the parameter sectionName I'm passing to the helper method.
I could do a switch for sectionName but I'm just wondering if there's a neater way of achieving this.
You haven't shown how the ActionLinkWithQueryString custom helper looks like but you could add an overload which takes a RouteValueDictionary instead of an anonymous object. And then it's easy:
var values = new RouteValueDictionary();
values["somedynamicname"] = "some dynamic value";
values["someotherdynamicname"] = "some other dynamic value";
and then:
#Html.ActionLinkWithQueryString(fv.Range, "Search", values);
Since you said that this is an extension method using ActionLink internally, there are overloads of ActionLink that take RouteValueDictionary instead of an anonymous object as route parameter.
And since RouteValueDictionary has a constructor that takes a IDictionary<string, object> it could be very easy to build it using LINQ from your model.
Please try this solution:
var routeValueDictionary = new RouteValueDictionary("");
routeValueDictionary.Add("keyName",value);
// Add any item you want!
#html.ActionLink("LinkName", "ActionName", "ControllerName",
routeValueDictionary , new{#class="form-control"})

How to encrypt URL parameters in MVC

I'm trying to encrypt the URL parameters by implementing an EncryptedActionLink that returns a link with an encrypted parameter "p" to a generic action "ResolveUrl". The controller should recieve the request and invoke the proper action, or redirect it to the actual action without showing later the unencrypted values at the address bar (RedirectToAction doesn't work because of this).
So far, I've done this extension method:
public static MvcHtmlString EncryptedActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
var RouteValueDictionary = new RouteValueDictionary(routeValues);
RouteValueDictionary.Add("actionName", actionName);
RouteValueDictionary.Add("noise", new Random().Next(5000,10000));
var routeValuesText = RouteTable.Routes.GetVirtualPath(null, RouteValueDictionary).VirtualPath;
var Encryption64 = new Encryption64();
var routeValuesTextCrypto = Encryption64.Encrypt(routeValuesText, "ABC123AB");
return htmlHelper.ActionLink(linkText, "ResolveUrl", controllerName, new { p = routeValuesTextCrypto }, htmlAttributes);
}
using this method, i get the following URL:
<%: Html.EncryptedActionLink("MyText", "MyAction", "MyContoller", new { Parameter1 = 123, Parameter2 = "String", Parameter3 = false }, null)%>
http://localhost:21536/MyContoller/ResolveUrl?p=iqo6yhy0Zl3jZXdMmnJ9KdvQhqCb5X6gg19%2FqZ8XUe19r5PJ6xO84plZr1GUHCHNY9h2SDO1o4CaF9W2DdmpywXooEQ1S0rNYjpnH4s3wb%2FqM8sGxoqAqyIoC%2F2nqW7U
Now, all my contollers inherits from ContollerBase. There I define the ResolveUrl Action as this:
public ActionResult ResolveUrl(String p)
{
var Encryption64 = new Encryption64();
var query = Encryption64.Decrypt(p, "ABC123AB");
if (query.Length > 2)
query = query.Substring(2);
var tokens = query.Split(new String [] { "&" }, StringSplitOptions.RemoveEmptyEntries);
var RouteValueDictionary = new RouteValueDictionary();
for (int i = 0; i < tokens.Count(); i++)
{
var centerPos = tokens[i].IndexOf("=");
RouteValueDictionary.Add(tokens[i].Substring(0,centerPos),tokens[i].Substring(centerPos+1));
}
Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod(RouteValueDictionary["actionName"].ToString());
var theParameters = theMethod.GetParameters();
var theParametersObject = new object[theParameters.Count()];
System.ComponentModel.TypeConverter converter = new System.ComponentModel.TypeConverter();
for (int i=0 ; i<theParameters.Count();i++)
{
theParametersObject[i] = converter.ConvertTo(RouteValueDictionary[theParameters[i].Name],theParameters[i].ParameterType);
}
return (ActionResult)theMethod.Invoke(this, theParametersObject);
}
the thing about that code is that the ResolveUrl doesn't work. First, when there are two implementatios for one action (POST/GET) then an exception is throwed. And the second thing that fails is the parameter type conversion (for exampte converting from string to an nullable type).
How can I encrypt the URL parameters? Is any of my code useful? What is the best way to do this?
if you are trying to encrypt url parameters (route values) you can use custom valuedataprovider that will automatically decrypt the value on action without showing unencrypted value in address bar.

MVC RouteLink being double encoded

I have the following link defined in a page that will be built based on the route defined in the web.config
<%= Html.RouteLink(product.DisplayName, "ShopProductNames", new {Id = product.Id, ProductName = product.DisplayName.Replace(' ', '-') }) %>
I need to URL encode the DisplayName in the URL of that link, however, when I add encoding in the following way:
<%= Html.RouteLink(product.DisplayName, "ShopProductNames", new {Id = product.Id, ProductName = Url.Encode(product.DisplayName.Replace(' ', '-')) }) %>
It double encodes my DisplayName (in the URL), and I get an error in IIS.
My DisplayName property is not being encoded before it's passed to the page. Also, RouteLink does not appear to be Url encoding for the rendered link by default as it's not picking up spaces or ampersands when the page is rendered.
Anyone know what I'm doing wrong?
UPDATE: I'm actually referring to the URL generated by RouteLink, not the link text itself
UPDATE 2: here is the route I'm using
routes.MapRoute(
"ShopProductNames",
"Shop/{productName}/p/{id}/{*nameInfo}",
new
{
controller = "Product",
action = "Detail"
}
);
Look at HtmlHelper.cs file, line 140:
internal static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues)
{
RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues);
VirtualPathData vpd = routeCollection.GetVirtualPath(requestContext, routeName, mergedRouteValues);
if (vpd == null) {
return null;
}
string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);
return modifiedUrl;
}
Url is created by routeCollection.GetVirtualPath() method (System.Web.Routing.dll). Using Reflector you'll see that it uses Uri.EscapeDataString() internally (System.Web.Routing.ParsedRoute.Bind method)

How to leave URL parameters unescaped in ASP.NET MVC?

I've noticed the returnurl URL parameter on the Stackoverflow login/logout links are not escaped but when I try to add path as a parameter to a route it gets escaped.
So /login?returnurl=/questions/ask shows /login?returnurl=%2fquestions%2fask and it's kind of ugly. How do I get it to not escape the returnurl value?
Here's what I'm doing in the code:
Html.ActionLink("Login", "Login", "Account", new { returnurl=Request.Path }, null)
How do I get it to not escape the
returnurl value
How's about this?
var url = Url.Action("Login", "Account", new {returnurl = Request.Path});
var unEncodedUrl = HttpUtility.UrlDecode(url);
Response.Write("<a href='" + unEncodedUrl + "'>...</a>");
Be sure that's what you want though, URL encoding has its purpose.
I understand one of the comments about encoding happening for a reason; this would only be an exception, not the rule.
Here's what I put together, how can it be improved?
public static string ActionLinkNoEscape(this HtmlHelper html, string linkText, string actionName, string controllerName, object values, object htmlAttributes)
{
RouteValueDictionary routeValues = new RouteValueDictionary(values);
RouteValueDictionary htmlValues = new RouteValueDictionary(htmlAttributes);
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext, RouteTable.Routes);
string url = urlHelper.Action(actionName, controllerName);
url += "?";
List<string> paramList = new List<string>();
foreach (KeyValuePair<string, object> pair in routeValues)
{
object value = pair.Value ?? "";
paramList.Add(String.Concat(pair.Key, "=", Convert.ToString(value, CultureInfo.InvariantCulture)));
}
url += String.Join("&", paramList.ToArray());
TagBuilder builder = new TagBuilder("a");
builder.InnerHtml = string.IsNullOrEmpty(linkText) ? "" : HttpUtility.HtmlEncode(linkText);
builder.MergeAttributes<string, object>(htmlValues);
builder.MergeAttribute("href", url);
return builder.ToString(TagRenderMode.Normal);
}
The parameter is not unescaped. You'll notice the URL:
http://stackoverflow.com/users/login?returnurl=%2fquestions%2fask
does actually work - SO is reading and unescaping that parameter as normal. If you wanted to include other out-of-bounds characters such as '&' in the parameter you would still have to escape them.
The trick is merely that the '/' character in particular does not need to be %-escaped in query parameters. It does have to be escaped in other contexts such as in a path part, so URLEncode always encodes it, to be safe.
If you just want the URL to look prettier, simply escape the parameter as normal (which you must do to escape all the other characters that must be handled correctly), and then do a string replace on '%2f' with '/'.
My solutionto a similar problem was to write my own extension. After digging around in the code I couldn't find a way to do it otherwise. Yours might look like this.
public static class HtmlHelperExtensions
{
public static string LoginLinkWithReturnUrl( this HtmlHelper helper,
string linkText,
string action,
string controller,
string returnUrl,
object htmlAttributes )
{
TagBuilder builder = new TagBuilder("a");
builder.Attributes.Add( "href",
string.Format( "/{0}/{1}?returnurl={2}",
controller,
action,
returnUrl ) );
var attrDict = new RouteValueDictionary( htmlAttributes );
builder.MergeAttributes( attrDict );
builder.InnerHtml = linkText;
return builder.ToString();
}
}
I think I had the same problem making and using a UrlHelper so I went with the string.Format mechanism instead. YMMV.
I don't believe there's a way around it that's built into the framework. The actual construction of the URL happens in the System.Web.Routing.ParsedRoute.Bind method and there aren't any conditions used to prevent the escaping.
Looks like an extension method is the way to go but one that is slightly more robust than the one mentioned previously.

Resources