Regarding writing custom html helper for hidden field with few good feature ASP.Net MVC - asp.net-mvc-3

this way i can write custom helper for hidden field
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Linq.Expressions;
namespace CustomHtmlHelpers.CustomHelpers
{
public static class CustomHiddenHelperModelBinding
{
//This overload accepts single expression as parameter.
public static MvcHtmlString Custom_HiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
return Custom_HiddenFor(helper, expression, null);
}
//This overload accepts expression and htmlAttributes object as parameter.
public static MvcHtmlString Custom_HiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
//Fetching the metadata related to expression. This includes name of the property, model value of the property as well.
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
//Fetching the property name.
string propertyName = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
//Creating a textarea tag using TagBuilder class.
TagBuilder hidden = new TagBuilder("input");
//Setting the type attribute to hidden to render hidden input field.
hidden.Attributes.Add("type", "hidden");
//Setting the name and id attribute.
hidden.Attributes.Add("name", propertyName);
hidden.Attributes.Add("id", propertyName);
//Setting the value attribute of textbox with model value if present.
if (metadata.Model != null)
{
hidden.Attributes.Add("value", metadata.Model.ToString());
}
//merging any htmlAttributes passed.
hidden.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(hidden.ToString(TagRenderMode.Normal));
}
}
}
later we can access it like
#Html.Custom_HiddenFor(Model => Model.hidden)
#Html.Custom_HiddenFor(Model => Model.hidden, new { #class = "hiddenClass" })
my objective to rewrite my own html helper for hidden field is to render value at client side as encrypted text and as well as tamper proof.
if anyone tamper data then i want to check at server side and if tamper then i will show friendly error message to user.
this is another sampe code for encryption with machine key but i am not sure the that code works fine in partial trust environment or not?
here is the sample code
string Protect(byte[] data)
{
if (data == null || data.Length == 0) return null;
return MachineKey.Encode(data, MachineKeyProtection.All);
}
byte[] Unprotect(string value)
{
if (String.IsNullOrWhiteSpace(value)) return null;
return MachineKey.Decode(value, MachineKeyProtection.All);
}
here’s the 4.5 usage (it supports a slightly more sophisticated usage)
-------------------------------------------------------------------------
const string MachineKeyPurpose = "MyApp:Username:{0}";
const string Anonymous = "<anonymous>";
string GetMachineKeyPurpose(IPrincipal user)
{
return String.Format(MachineKeyPurpose,
user.Identity.IsAuthenticated ? user.Identity.Name : Anonymous);
}
string Protect(byte[] data)
{
if (data == null || data.Length == 0) return null;
var purpose = GetMachineKeyPurpose(Thread.CurrentPrincipal);
var value = MachineKey.Protect(data, purpose);
return Convert.ToBase64String(value);
}
byte[] Unprotect(string value)
{
if (String.IsNullOrWhiteSpace(value)) return null;
var purpose = GetMachineKeyPurpose(Thread.CurrentPrincipal);
var bytes = Convert.FromBase64String(value);
return MachineKey.Unprotect(bytes, purpose);
}
another way to encrypt
To generate a random string, use the RNGCryptoServiceProvider.
public string GenerateSalt(int length)
{
var rng = new RNGCryptoServiceProvider();
var buffer = new byte[length];
rng.GetBytes(buffer);
return Convert.ToBase64String(buffer);
}
Now we can generate a hashed password using the function below
public virtual string CreatePasswordHash(string password, string saltkey, string passwordFormat = "SHA1")
{
if (String.IsNullOrEmpty(passwordFormat))
passwordFormat = "SHA1";
string saltAndPassword = String.Concat(password, saltkey);
string hashedPassword =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPassword, passwordFormat);
return hashedPassword;
}
so guide me how can i rewrite my own custom html helper which will encrypt data in most secure way and later which can be check to make sure the
value is tamper or not at client side.
i have another requirement to easily decrypt the value when form will post and action method will be called.
i want to decrypt the encrypted value using a attribute on action method. i want to fire a function will fire before action method and decrypt
value before data de-serialize to model or to action method argument.
is it possible ?
i want that my action method would look like
[HttpPost]
[Decrypt]
public ActionResult Save(string personname, string email)
{
return View();
}
or
[HttpPost]
[Decrypt]
public ActionResult Save(Person oPerson)
{
return View();
}
i want that my [Decrypt] attribute will fire a method call decypt and pass all value to decypt() function just before action method Save would invoke.
if decypt() function found any encrypted value then it will decrypt and de-serialize decypted value to model or to action method argument.
so i have three requirements
1) i want to write a custom html helper which will render hidden field with encrypted value.
2) when data post to server there i can detect that value has been tamper or not.
if tamper then a error page with proper message will be shown to user.
3)
action method will have custom attribute which will invoke a function before action method execution.
that function will decrypt value if any encrypted value found. after decryption the decrypted value will be properly De-serialize to model
or other action method argument properly.
i am new in MVC. so guide me how should i move forward with sample code. thanks

What about #Html.AntiForgeryToken()?
"Generates a hidden form field (anti-forgery token) that is validated when the form is submitted." Developer Network
Example:
1. In your markup add #Html.AntiForgeryToken() to the form.
2. Add [ValidateAntiForgeryToken] attribute your controller action.
Developer Network

Related

To disable Get Method if Sensitive Information is passed

Our Application (MVC Based) accepts user payment information update request over GET method.Default method used by the application is POST.
Currently if we pass any sensitive information over a GET Method via Querystring, then Request sucessfully works.The reason is that it hits the same Edit Action method in Controller
[HttpGet]
[ValidateRequest(true)]
public ActionResult Edit (parameters)
But what we want is that Any request with sensitive information (like Credit Card etc.) sent over a GET method should be rejected by the application.
Anyhow can we reject GET method through Routing if sensitive information is passed? Please suggest valid approach.
My current route that calls Action is mentioned below:
routes.MapRoute("ChargeInformation", "ChargeInformationt.aspx/{seq}", new { controller = "Payment", action = "Edit", seq = UrlParameter.Optional });
Routing's only responsibility is to map URLs to route values and from route values back to URLs. It is a separate concern than authorizing the request. In fact, the built-in routing extension methods (MapRoute, MapPageRoute, and IgnoreRoute) completely ignore the incoming query string.
For request authorization, MVC has an IAuthorizationFilter interface that you can hook into. You can also (optionally) combine it with an attribute to make it run conditionally on specific action methods, as shown below.
In this case, you just want to reject specific query string key names that are passed into the request. It is unclear what action you wish to take in this case, so I am just setting to HTTP 403 forbidden as an example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Mvc;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DisallowQueryStringKeysAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly IEnumerable<string> keysSplit;
public DisallowQueryStringKeysAttribute(string keys)
{
this.keysSplit = SplitString(keys);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var queryStringKeys = filterContext.HttpContext.Request.QueryString.AllKeys;
// If any of the current query string keys overlap with the non-authorized keys
if (queryStringKeys.Intersect(this.keysSplit, StringComparer.OrdinalIgnoreCase).Any())
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
// You must set the result property to a handler to run to tell the
// framework that the filter should do something other than run the
// action method. In this case, we just set it to an empty result,
// which implements the null object pattern. You could (if so inclined),
// make a class to set the status code or do something else
// (such as redirect) to indicate that the request is invalid.
filterContext.Result = new EmptyResult();
}
}
private string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
Usage
[HttpGet]
[ValidateRequest(true)]
[DisallowQueryStringKeys("creditCard, password")]
public ActionResult Edit (string creditCard, string password)

Passing parameter from view to controller when button is clicked

I have created my own extension as:
public static MvcHtmlString hSearch(this HtmlHelper helper, string labelName, string labelCaption, string textName, string textValue, string tableName, string buttonId,
string actionName, string controllerName, object routeValues, object htmlAttributes)
{
var textbuilder = new TagBuilder("input");
textbuilder.MergeAttribute("id", textName);
textbuilder.MergeAttribute("name", textName);
textbuilder.MergeAttribute("value", textValue);
textbuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
ModelMetadata metadata = ModelMetadata.FromStringExpression(labelName, helper.ViewData);
String innerText = labelCaption ?? (metadata.DisplayName ?? (metadata.PropertyName ?? labelName.Split('.').Last()));
if (String.IsNullOrEmpty(innerText))
{
return MvcHtmlString.Empty;
}
TagBuilder labelbuilder = new TagBuilder("label");
labelbuilder.Attributes.Add("for", TagBuilder.CreateSanitizedId(helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(labelName)));
labelbuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
labelbuilder.SetInnerText(innerText);
//return new MvcHtmlString(textbuilder.ToString());
var buttonBuilder = new TagBuilder("button");
buttonBuilder.MergeAttribute("id", buttonId);
buttonBuilder.SetInnerText(buttonId);
var formBuilder = new TagBuilder("form");
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
formBuilder.Attributes.Add("action", urlHelper.Action(actionName, controllerName, routeValues));
formBuilder.Attributes.Add("method", "Post");
formBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
formBuilder.InnerHtml = labelbuilder.ToString() + textbuilder.ToString() + buttonBuilder.ToString();
return new MvcHtmlString(formBuilder.ToString());
}
I used the extensions in view as:
#Html.hSearch("lblSrch", "Company", "companyName", (string)TempData["cName"], "CHComp", "Search", "Fetch", "Home", null, null)
Now I want to pass tableName when I click the button to the controller.. my controller looks like this:
public ActionResult Fetch(string search, string tablename)
{
var c = cbo.fetchData(search, tablename);
return PartialView(c.ToList());
}
Waiting for reply.. Thanks..
You haven't given us the code for your helper, but at a guess it writes out a label, a text field (textName), and a button. If this is the case, it will post / get companyName=someValue via HTTP back to your controller.
You would typically need to add a FormCollection to your controller if the fields are dynamically sent from the view. Alternatively, why not keep the name of the text search input static, e.g. name="search", which will bind to your controller's parameter of the same name.
Edit
You can pass tableName back to the controller in a hidden field (<input type='hidden' name='tableName' value='{tableNameGoesHere}')
But as per above, your search string will have different names- the model binder isn't going to recognise it as string search.

How can i pass a Query string parameter to my [Remote] data annotation validation

i have an object named VisitLabResult that should not have two records with the same LabTestID and VisitID ,, so to force this validation i have create a [remote] validation on the LabTestID property as follow:-
[Required]
[Remote("checkexsistence", "VisitLabResult",AdditionalFields= "VisitID")]
public int LabTestID { get; set; }
which will call the following action method:-
public JsonResult checkexsistence(int LabTestID, int VisitID)
{
var result = repository.checkexsistence(LabTestID, VisitID);
if (result == null)
{
var type = true;
return Json(type, JsonRequestBehavior.AllowGet);
}
else {
var type = false;
return Json(type, JsonRequestBehavior.AllowGet);
}
}
and the repository method is :-
public VisitLabResult checkexsistence(int labtestid, int visitid =20)
{
return (from vlr in entities.VisitLabResults
where (vlr.VisitID == visitid && vlr.LabTestID == labtestid)
select vlr).FirstOrDefault();}
Currently the visitid parameter which is mentioed in the AdditionalFields= "VisitID" inside the Remote data annotation is found in the query string for calling the CreateGet action method the query string looks as /VisitLabResult/Create?visitid=20.
But the above is not working at all ; no error will be displayed incase the visitid and labtestid are already exists, so how can i fix this ???
DataAnnotations are more about making sure that the data itself is potentially valid. You should not be trying to determine uniqueness to the rest of the items in you database with a DataAnnotation. If you really want to go down this route, I suggest looking into making custom validators.
Is field VisitID being a part(property) of your model class? The additional field has to be a part of Your model class. For example, if the field Id is in your model(where LabTestID property is) then you shout annotate LabTestID remote like this:
[Remote("checkexsistence", "VisitLabResult",AdditionalFields= "Id")]

What's DisplayAttribute.GroupName property used for?

I'm trying to figure out the valid usages of DisplayAttribute.GroupName property.
MSDN says:
A value that is used to group fields in the UI.
but I wouldn't call it a comprehensive explanation. It makes me think that GroupName can be used to create groupboxes around certain fields. But then the remark:
Do not use this property to get the value of the GroupName property.
Use the GetDescription method instead. A null value or empty string is
valid.
seems to contradict it.
So what is this property for and should I use it (probably with custom template or custom ModelMetadataProvider) in order to render groupboxes around my fields?
In the MVC RTM source code there is no sign of usage.
The "GetDescription" remark might be a copy/paste error in the documentation (each string property seems to have a GetXXX counterpart that returns a localizable value), so it should be most probably "GetGroupName" in this case.
UPDATE:
I would use it exactly for that: group fields together that belong together from the UI point-of-view. As this is just data annotation on the model, it declares only that these fields belong to one logical group "somehow" on the UI, the but concrete presentation details depend on the "UI engine" that displays the model based on the metadata.
I think the most meaningful way to "render" this on the UI is exactly what you said: wrapping the grouped fields into a section or fieldset.
Of course there might be future extensions of MVC or other custom extensions that do some kind of grouping on the UI "automatically" (without writing custom code that examines the metadata and generates the sections) based on this attribute property. But I'm quite sure that such an extension would do something very similar that you would do currently.
I ended up writing this class to make the GroupName more easily accessible:
public class ExtendedDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
public const string Key_GroupName = "GroupName";
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
ModelMetadata modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
DisplayAttribute displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault();
if (displayAttribute != null)
modelMetadata.AdditionalValues[ExtendedDataAnnotationsModelMetadataProvider.Key_GroupName] = displayAttribute.GroupName;
return modelMetadata;
}
}
And this extension method:
public static string GetGroupName(this ModelMetadata modelMetadata)
{
if (modelMetadata.AdditionalValues.ContainsKey(ExtendedDataAnnotationsModelMetadataProvider.Key_GroupName))
return (modelMetadata.AdditionalValues[ExtendedDataAnnotationsModelMetadataProvider.Key_GroupName] as string);
return null;
}
Source: http://bradwilson.typepad.com/blog/2010/01/why-you-dont-need-modelmetadataattributes.html
How About This !!! Must Work :
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
namespace System.Web.Mvc
{
public static class DisplayGroup
{
public static MvcHtmlString DisplayGroupName(this HtmlHelper helper, string groupName)
{
return MvcHtmlString.Create(groupName);
}
public static MvcHtmlString DisplayGroupNameFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var type = typeof(TModel);
PropertyInfo propertyInfo = null;
var member = (MemberExpression)expression.Body;
var property = (PropertyInfo)member.Member;
var name = property.Name;
var metadataTypeInfo = type.GetCustomAttribute<MetadataTypeAttribute>();
if (metadataTypeInfo != null)
{
var metadataType = metadataTypeInfo.MetadataClassType;
propertyInfo = metadataType.GetProperties().Where(x => x.Name == name).FirstOrDefault();
if (propertyInfo == null)
{
propertyInfo = type.GetProperties().Where(x => x.Name == name).FirstOrDefault();
}
}
else
{
propertyInfo = type.GetProperties().Where(x => x.Name == name).FirstOrDefault();
}
string output = "";
var dattr = propertyInfo.GetCustomAttribute<DisplayAttribute>();
if (dattr != null)
{
if (dattr.GroupName == null)
{
output = propertyInfo.Name;
}
else
{
output = dattr.GroupName;
}
}
else
{
output = propertyInfo.Name;
}
return MvcHtmlString.Create(output);
}
}
}
public class MyModel
{
[Display(Name = "Number",GroupName="Invoice")]
string InvNo { get; set; }
}
and then simply write :
#Html.DisplayGroupNameFor(x => x.InvNo)
Note :
NameSpace should be : System.Web.Mvc
Update :
The cool thing is that , if you have a MetaDataType class defined for your dataAnnotation , then also this will work as expected.

MVC Model Binding a Complex Type to a Simple Type and Vice Versa

Here's a scenario:
I have an autocomplete plugin (custom) that keeps a hidden field of JSON objects (using a specific struct).
I've created an Html helper that helps me easily bind to a specific custom model (basically, it has a JSON property that is for two-way binding and a property that lets me deserialize the JSON into the appropriate struct):
public class AutoCompleteModel {
public string JSON { get; set; }
public IEnumerable<Person> People {
get {
return new JavaScriptSerializer().Deserialize<Person>(this.JSON);
}
set {
this.JSON = new JavaScriptSerializer().Serialize(value);
}
}
}
This works great and I can model bind using the default binder #Html.Autocomplete(viewModel => viewModel.AutoCompleteModelTest). The HTML helper generates HTML like:
<input type="text" id="AutoCompleteModelTest_ac" name="AutoCompleteModelTest_ac" value="" />
<input type="hidden" id="AutoCompleteModelTest_JSON" name="AutoCompleteModelTest.JSON" value="{JSON}" />
The problem is this is not the best way for consumers. They have to manually set the People property to an array of Person structs. In my data layer, my domain objects probably will not be storing the full struct, only the person's ID (a corporate ID). The autocomplete will take care of looking up the person itself if only given an ID.
The best scenario will be to call it like this:
#Html.Autocomplete(domainObject => domainObject.PersonID) or
#Html.Autocomplete(domainObject => domainObject.ListOfPersonIDs
I would like it to work against the string property AND against the custom AutoCompleteModel. The autocompleter only updates a single hidden field, and that field name is passed back on postback (the value looks like: [{ "Id":"12345", "FullName":"A Name"},{ "Id":"12347", "FullName":"Another Name" }]).
The problem is, of course, that those domain object properties only have an ID or array of IDs, not a full Person struct (so cannot be directly serialized into JSON). In the HTML helper, I can transform those property values into a struct, but I don't know how to transform it back into a simple type on POST. The solution I need would transform an ID into a new Person struct on page load, serializing it into the hidden field. On POST, it would deserialize the generated JSON back into a simple array of IDs.
Is a custom model binder the solution I need? How can I tell it to work both with a custom model AND simple types (because I don't want it applied to EVERY string property, just need it to deal with the values given by the HTML helper).
I figured it out, it's possible!
To clarify, I needed to: transform a string or string array (of IDs) into a JSON structure for my hidden field value, then on post back, deserialize the JSON in the hidden field and transform the struct back into a simple string or string array (of IDs) for my domain object's property.
Step 1: Create a HTML helper
I had done this already, but only for accepting my custom AutoCompleteModel type. I needed one for a string and an Enumerable of string type.
All I did was generate my Person struct(s) from the value of the property and serialize them into JSON for the hidden field the Autocompleter uses (this is an example of the string helper, I also have a nearly identical one for IEnumerable<string>):
public static MvcHtmlString AutoComplete<TModel>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, string>> idProp)
where TModel : class
{
TModel model = htmlHelper.ViewData.Model;
string id = idProp.Compile().Invoke(model);
string propertyName = idProp.GetPropertyName();
Person[] people = new Person[] {
new Person() { ID = id }
};
// Don't name the textbox the same name as the property,
// otherwise the value will be whatever the textbox is,
// if you care.
MvcHtmlString textBox = htmlHelper.TextBox(propertyName + "_ac", string.Empty);
// For me, the JSON is the value I want to postback
MvcHtmlString hidden = htmlHelper.Hidden(propertyName, new JavaScriptSerializer().Serialize(people));
return MvcHtmlString.Create(
"<span class=\"AutoComplete\">" +
textBox.ToHtmlString() +
hidden.ToHtmlString() +
"</span>");
}
Usage: #Html.AutoComplete(model => model.ID)
Step 2: Create a custom model binder
The crux of my issue was that I needed this binder to only apply to certain properties, and they were strings or string arrays.
I was inspired by this article because it used Generics. I decided, hey, we can just ask people what property they want to apply the binder for.
public class AutoCompleteBinder<T> : DefaultModelBinder
where T : class
{
private IEnumerable<string> PropertyNames { get; set; }
public AutoCompleteBinder(params Expression<Func<T, object>>[] idProperties)
{
this.PropertyNames = idProperties.Select(x => x.GetPropertyName());
}
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
var submittedValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (submittedValue != null && this.PropertyNames.Contains(propertyDescriptor.Name))
{
string json = submittedValue.AttemptedValue;
Person[] people = new JavaScriptSerializer().Deserialize<Person[]>(json);
if (people != null && people.Any())
{
string[] IDs = people.Where(x => !string.IsNullOrEmpty(x.ID)).Select(x => x.ID).ToArray();
bool isArray = bindingContext.ModelType != typeof(string) &&
(bindingContext.ModelType == typeof(string[]) ||
bindingContext.ModelType.HasInterface<IEnumerable>());
if (IDs.Count() == 1 && !isArray)
return IDs.First(); // return string
else if (IDs.Count() > 0 && isArray)
return IDs.ToArray(); // return string[]
else
return null;
}
else
{
return null;
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
GetPropertyName() (translate LINQ expression into a string, i.e. m => m.ID = ID) and HasInterface() are just two utility methods I have.
Step 3: Register
Register the binder on your domain objects and their properties in Application_Start:
ModelBinders.Binders.Add(typeof(Employee), new AutoCompleteBinder<Employee>(e => e.ID, e => e.TeamIDs));
It's only a little bit annoying to have to register the binder for specific properties, but it's not the end of the world and provides a nice, smooth experience working with my autocompleter.
Any comments are welcome.

Resources