How to encrypt URL parameters in MVC - asp.net-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.

Related

get unobtrusive validation attributes by ViewModel Type

I need something like
var dict = html.GetUnobtrusiveValidationAttributes<ViewModel1>(propName);
and I got it working like this:
public static IHtmlString GetVld<TModel>(this HtmlHelper html)
{
...
foreach (var prop in typeof(TModel).GetProperties())
{
var attributes = new Dictionary<string, object>();
Func<string, ModelMetadata, IEnumerable<ModelClientValidationRule>> ClientValidationRuleFactory
= (name, metadata) => ModelValidatorProviders.Providers.GetValidators(metadata ?? ModelMetadata.FromStringExpression(name, html.ViewData), html.ViewContext).SelectMany(v => v.GetClientValidationRules());
var clientRules = ClientValidationRuleFactory(prop.Name, null);
UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules, attributes);
the only problem is that I have to call this in a view with #model in order for it to work, otherwise attributes will always be empty
#model MyVM // I want to make it work without this line
#Html.GetVld<MyVM>()
I tried doing:
var html2 = new HtmlHelper<TModel>(html.ViewContext, html.ViewDataContainer) and using html2 instead of html but it won't help.

Troubleshoot MVC model binding failure - argument is null in controller

I am trying to POST an object from a WebJob to an MVC 4 controller. I am using Entity Framework. In the controller, I cannot get the object to bind properly (the argument is null). I have looked at many tutorials and it seems like my code should work.
Model (does this need to be in a specific namespace for EF to find it?):
public class CreateListingObject
{
public Listing listing;
public List<GalleryImage> images;
public CreateListingObject()
{
listing = new Listing();
images = new List<GalleryImage>();
}
}
public struct GalleryImage
{
public string picURL;
public string caption;
}
POST:
public void PostListing(CreateListingObject o)
{
Console.WriteLine("Posting listing: {0}", o.listing.Title);
HttpClient _httpClient = new HttpClient();
Uri uri = new Uri(_serviceUri, "/Automaton/CreateTestListing");
string json = BizbotHelper.SerializeJson(o);
HttpResponseMessage response = BizbotHelper.SendRequest(_httpClient, HttpMethod.Post, uri, json);
string r = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
}
SendRequest (thank you Azure search samples):
public static HttpResponseMessage SendRequest(HttpClient client, HttpMethod method, Uri uri, string json = null)
{
UriBuilder builder = new UriBuilder(uri);
//string separator = string.IsNullOrWhiteSpace(builder.Query) ? string.Empty : "&";
//builder.Query = builder.Query.TrimStart('?') + separator + ApiVersionString;
var request = new HttpRequestMessage(method, builder.Uri);
if (json != null)
{
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
return client.SendAsync(request).Result;
}
Controller Action fragment (o is an empty object here):
[HttpPost]
public ActionResult CreateTestListing(CreateListingObject o)
{
Listing li = o.listing;
I have confirmed that if I post a simple object using the same code, everything works as expected.
Instead of sending a CreateListingObject in PostListing, I send this instead:
var test = new
{
data = "hi mom"
};
And change my action to, then the argument gets bound and I get valid data:
[HttpPost]
public ActionResult CreateTestListing(string data)
{
I have also checked the serialization of my CreateListingObject in the WebJob, and it is fully populated as I expect. This leads me to suspect that I am falling afoul of the default ModelBinder.

MVC Html.ActionLink removes empty querystring parameter from URL

I'm using the Html.ActionLink(string linkText, string actionName, object routeValues) overload to send some params to an Action Method..
Sometimes I need to pass an empty parameter value like: ?item1=&item2=value
I'm specifying the param in the anonymous type I create and pass as routeValues, but both null and string.Empty result in a URL that omits the empty item1 param.
new { item1 = null, item2 = "value" }
new { item1 = string.Empty, item2 = "value" }
new { item1 = "", item2 = "value" }
// all result in:
?item2=value
I can create the URL manually but I want to use the helper method.
Suggestions?
Create an EmptyParameter class as follows:
public class EmptyParameter
{
public override string ToString()
{
return String.Empty;
}
}
Then:
#Html.ActionLink("Action",
"Controller",
new { item1 = new EmptyParameter(), item2 = "value" });

ASP.NET MVC - Pass array object as a route value within Html.ActionLink(...)

I have a method that returns an array (string[]) and I'm trying to pass this array of strings into an Action Link so that it will create a query string similar to:
/Controller/Action?str=val1&str=val2&str=val3...etc
But when I pass new { str = GetStringArray() } I get the following url:
/Controller/Action?str=System.String%5B%5D
So basically it's taking my string[] and running .ToString() on it to get the value.
Any ideas? Thanks!
Try creating a RouteValueDictionary holding your values. You'll have to give each entry a different key.
<% var rv = new RouteValueDictionary();
var strings = GetStringArray();
for (int i = 0; i < strings.Length; ++i)
{
rv["str[" + i + "]"] = strings[i];
}
%>
<%= Html.ActionLink( "Link", "Action", "Controller", rv, null ) %>
will give you a link like
<a href='/Controller/Action?str=val0&str=val1&...'>Link</a>
EDIT: MVC2 changed the ValueProvider interface to make my original answer obsolete. You should use a model with an array of strings as a property.
public class Model
{
public string Str[] { get; set; }
}
Then the model binder will populate your model with the values that you pass in the URL.
public ActionResult Action( Model model )
{
var str0 = model.Str[0];
}
This really annoyed me so with inspiration from Scott Hanselman I wrote the following (fluent) extension method:
public static RedirectToRouteResult WithRouteValue(
this RedirectToRouteResult result,
string key,
object value)
{
if (value == null)
throw new ArgumentException("value cannot be null");
result.RouteValues.Add(key, value);
return result;
}
public static RedirectToRouteResult WithRouteValue<T>(
this RedirectToRouteResult result,
string key,
IEnumerable<T> values)
{
if (result.RouteValues.Keys.Any(k => k.StartsWith(key + "[")))
throw new ArgumentException("Key already exists in collection");
if (values == null)
throw new ArgumentNullException("values cannot be null");
var valuesList = values.ToList();
for (int i = 0; i < valuesList.Count; i++)
{
result.RouteValues.Add(String.Format("{0}[{1}]", key, i), valuesList[i]);
}
return result;
}
Call like so:
return this.RedirectToAction("Index", "Home")
.WithRouteValue("id", 1)
.WithRouteValue("list", new[] { 1, 2, 3 });
Another solution that just came to my mind:
string url = "/Controller/Action?iVal=5&str=" + string.Join("&str=", strArray);
This is dirty and you should test it before using it, but it should work nevertheless. Hope this helps.
There is a library called Unbinder, which you can use to insert complex objects into routes/urls.
It works like this:
using Unbound;
Unbinder u = new Unbinder();
string url = Url.RouteUrl("routeName", new RouteValueDictionary(u.Unbind(YourComplexObject)));
This is a HelperExtension solving array and IEnumerable properties troubles :
public static class AjaxHelperExtensions
{
public static MvcHtmlString ActionLinkWithCollectionModel(this AjaxHelper ajaxHelper, string linkText, string actionName, object model, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)
{
var rv = new RouteValueDictionary();
foreach (var property in model.GetType().GetProperties())
{
if (typeof(ICollection).IsAssignableFrom(property.PropertyType))
{
var s = ((IEnumerable<object>)property.GetValue(model));
if (s != null && s.Any())
{
var values = s.Select(p => p.ToString()).Where(p => !string.IsNullOrEmpty(p)).ToList();
for (var i = 0; i < values.Count(); i++)
rv.Add(string.Concat(property.Name, "[", i, "]"), values[i]);
}
}
else
{
var value = property.GetGetMethod().Invoke(model, null) == null ? "" : property.GetGetMethod().Invoke(model, null).ToString();
if (!string.IsNullOrEmpty(value))
rv.Add(property.Name, value);
}
}
return System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(ajaxHelper, linkText, actionName, rv, ajaxOptions, htmlAttributes);
}
}
I'd use POST for an array. Aside from being ugly and an abuse of GET, you risk running out of URL space (believe it or not).
Assuming a 2000 byte limit. The query string overhead (&str=) reduces you to ~300 bytes of actual data (assuming the rest of the url is 0 bytes).

How do I access query string parameters in 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"

Resources