Let's say I have the following selectlist (Countries) in a ViewModel:
//..
private static string[] _countries = new[] {
"USA",
"Canada",
"Japan"
};
//...
SelectList Countries = new SelectList(_countries, dinner.Country);
//...
And I render a dropdown list in the following fashion:
<%: Html.DropDownListFor(m => m.Dinner.Country, Model.Countries) %>
I noticed that using firebug, I can inject my own values into the DropDownList and that value may be inserted into the database.
What is the best way to validate that there are no injected values (preferably a DRY method)?
I would recommend taking advantage of DataAnnotations and create your own custom validation attribute.
This provides a way to encapsulate your validation logic (satisfying your DRY requirement), and will be applied server-side (preventing html manipulations like the one you described).
You should always validate your data server side anyways before inserting in the DB. If you had a key constraint it wouldn't be such an issue because the update or insert would fail. In this case though you should have a server side business rule to validate your object before doing the SQL call.
Since your building a list from a static list of items, the list should be available to your business layer so that you can compare against it to make sure that the value contained in your model is valid. You can add a method to your object such as IsValid or something that would do a quick validation and check that the values do exist for these hard coded selections.
Related
From my controller I send a ViewModel with a collection of strings to be used in a <select> tag like so...
Controller:
var model = new InviteViewModel
{
SelectItems = new SelectViewModel
{
Companies = _companyRepository.GetCompanyNames()
}
};
Razor View:
<select class="form-control company_select" asp-for="Company" asp-items="#(new SelectList(Model.SelectItems.Companies))"></select>
This works perfectly and will display all the items in the drop down box. However when I go to submit the form the Companies object will be null and when the View is sent back I get a null reference exception. Normally I would create a hidden <input> tag to hold the value, but how can I do this with a collection?
There are so many ways to do this.
It seems like you may have a fundamental misunderstanding of the disconnect between Razor and html. Razor executes server side, and its result is simply a string that gets written to the response stream. Once written, razor's scope is gone and cannot hold data.
One option would be to store the collection in the application cache or session cache with a guid as the dictionary key, and then use a hidden input for the cache key. When the view is being recreated you would then have access to the server and could gather the collection.
This makes the assumption that the collection hasn't changed during the time the view was active, which given some user habits could have been a long time. There should also be some sort of metrics used when caching to invalidate old data, if this is the route you take.
Another option is to simply regenerate the collection from wherever (database?) it came from.
Lastly, you mention that the view is being returned with the empty collection, are you returning a view from a post method? That is bad practice. Look up the "post-redirect-get pattern" for why and how to avoid it.
Is there a way to pass entire object from ASP.NET MVC 5 View to a Controller? This is my situation:
I have a View that displays all rows from a DB table
The view's model is IEnumerable
Each row has a link after it's data that leads to the scaffolded UPDATE view
Is there a way to pass the entire object to the Update controller method so it would initially fill the form inputs with the old data? Something like:
#Html.Action("Update me!", "Update", new { objectFromModelList })
And then in the controller
public ActionResult Update(MyType parameter)
{
return View(parameter);
}
Or something like that. Please help, I am new to this and can't find the answer anywhere.
Your objects could be so big! Query string's has a limitation on how much data you can pass via those based on the browser. You should consider passing a unique id value (of the record) and using which get the entire record from db in your action method and pass that to the view.
#foreach(var item in SomeCollection)
{
<tr>
<td> #Html.Action("Update me!", "Update", new { id = item.Id }) </td>
</tr>
}
and in the action method
public ActionResult Update(int id)
{
var item = GetItemFromId(id);
return View(item);
}
Assuming GetItemFromId method returns the method/view model from the unique id value. Basically you get the entire record using this unique id from your db table/repository.
Assuming that your Update View isn't of type IEnumerable...
You just need to pass the ID of the record that you want to send to the Update view...
Like so:
#Html.Action("Update me!", "Update", new { id = item.ID })
Then your Update action would look like this:
[HttpGet]
public ActionResult Update(int id)
{
var parameter = db/* connection string variable */.TableName.Find(id);
return View(parameter);
}
Then your link should work appropriately.
Hope this helps!
I have searched myself and the best way, aside from passing the ID, that I have found is to store any other variables that you might need into hidden input fields or HTML5 tags. Then you can script a way to handle any button/link click events. This way you can store vital object properties of each record and easily pass them back to a controller. Think Client-side here, Once the data ends up Client-Side, use Client-Side tools to handle and pass it back to server side/controller.
I do something similar with a type of library reservation system that allows users to reserve items on available dates. I pass all available records to the view. Each record has a few fields that I want to hold onto including the ID for users reference. When the user clicks the button, I collect the needed fields.
You could use HTML5 form input fields that are hidden or you could just use JavaScript to collect those values using GetElementByID. An example of this would be to store the ID in the div wrapper. Then have another div hold a sub parameter. You can use Javascript to find the record ID and then get the second div by it's id. Example would be get the id NameRecord from XRecord where X = the ID passed.
I then pass those values to the controller, instantiate a new class/object for the reservation. The new class object also has the item class/object as a property. For example consider the following;
var reservation = new Reservation
{
myKit = new ResourceKit()
};
After that, you can store it in a session if you need to build on it. In my case I am holding it in a session because I allow the user to check availability/dates. These items are a physical resources that gets checked out similar to a library and are transferred via office mail.
If you dont mind the data sitting client-side, you can store it using LocalStorage and JavaScript. This type of data isnt secure at all much like a cookie. One of the ways that I have used this is to set site preferences. Users can select a color scheme and those preferences are stored in LocalStorage. That way when the return to the site those preferences remain. This is a key attribute of LocalStorage and might not be applicable to your needs/circumstances.
this is the more or less the schema i want to generate my dynamic form on based on the fields above. i am going to add the direction , max size, default value and like wise some more fields in it. i am looking for recommended ways and methods in asp.net mvc for generating dynamic fields at runtime.
1) if i design my own engine for it then how? i am interested on that also but this is the last thing i am looking at. method to apply validation is very important in my scenario
2) any framework that may lessen the working time? or anything else?
I'll describe the generic approach, I don't want to code it for you.
Create meta class to describe each field (type, name, maxlength, null value handling, data source for combos, etc.)
Load the data from database and preprocess it
Populate the ViewBag with sanitized values
Create helper that will generated the control specific code
Html.ControlFor("Name", metadata);
Loop in view over the metadata collection.
which will generate textbox, combobox, etc.
Remeber that MVC form handling works over list of key-values, it's the Binder feature that converts it to objects. Saving data won't be difficult (dynamically created INSERT, UPDATE statement, ...).
I have a 3 layered system of presentation, logic, and data access. In my logic layer, I have a Validate() method which makes sure that the data that's about to get forwarded to the data access layer is valid for the database (no nulls where nulls are not allowed, and so on).
On top of that, in the .aspx presentation layer, we have some user-friendly error checking and validation, directly checking the controls on the web form. My problem is the ValidateInput() method in the code behind which checks the controls, it's several hundreds of lines long and really annoying to maintain. The code for checking the data is far far longer than the code that does the actual work.
What I've got looks like this:
private List<string> ValidateInput()
{
List<string> errormessages = new List<string>();
if (LastNameETextBox.Text.Trim() == String.Empty)
{
errormessages.Add("Last name required.");
}
if (FirstNameETextBox.Text.Trim() == String.Empty)
{
errormessages.Add("First name required.");
}
//etc. etc.
}
We have a nice styled notification box hidden in the master page that gets turned from Visible false to true when we call it, creating the "illusion" of an overlaying box. It looks really nice and works really well so we want to use it. The idea is that we gather up all the errors for the whole form, put them in a list, and then send that list to the notification box, which then gives you all the errors in one nice list.
But the Validate() is just a torturous amount of "if" statements and it's hard to keep track of. Is this just the nature of input validation, or is there some other, better way of handling this?
I think you can able to avoid using these kind of If statements using a Generic function.
My suggestion is to define a function like this
private List<string> ValidateInput(string ErrorMessage, TextBox txtInput, ValidatationType validationType)
{
List<string> errormessages = new List<string>();
if (validatationType == ValidationType.NoNullValues)
{
if (txtInput.Text.Equals(String.Empty))
{
errormessages.Add(ErrorMessage);
}
}
if (validatationType == ValidationType.Integer)
{
int number;
if (Int32.TryParse(value, out number))
{
errormessages.Add(ErrorMessage);
}
}
// etc. etc.
}
Enum ValidationType
enum ValidationType
{
NoNullValues,
Integer,
// etc
}
Please modify the function. Also checks the syntax, I am using notepad to write the code.
This approach also helps you to achieve re-useabilty if you are use all validation methods in a separate class.
Thanks
Couple of the different ways that I had handled it (in different projects) :
Use custom control library - controls were simply extending existing ASP.NET/3rd party controls to add validation logic which was controlled by properties such as IsMandatory, MandatoryMessage etc. Properties were typically set at the design-time (and were not backed by view-state). The validation logic would accumulate error messages in the message collection exposed by base page. Another variant had used user controls combining ASP.NET controls along with validators but frankly speaking, ASP.NET validators sucks.
Essentially a base page class had a validation logic that would traverse the control collection within the page and then perform validation based on control type. The page would offer a registry to register control to validate, type of validations and error message to display. There was also method to un-register/skip control validation that was used primarily to skip control traversal within control such as repeater/grid-view.
For client-side(browser side) validation, I had used jquery validation (or own JS library). In both cases above, the validation logic would also emit necessary start-up scripts to set-up the client-side validation - however, #2 could generate a single start-up script (with less size) while #1 entails generating a start-up script per control instance. From flexibility perspective, #1 is better and fits nicely in control based development approach. #2 centralizes your validation logic at one central place (i.e. a base page) which can be very handy.
a) Create an validation type enum to enumerate all types of validation you want to perform. (In your code you are doing string null checking for both controls which are both textboxes, its unnecessary repetition of code logic)
b) Create a class of constants / or resource file that holds all the error messages.
c) Write a function that takes a control, Validation type enum, arguments for validation and error message id. It will check the type of control and perform validation as indicated by enum. If error, error message is added to your "errorMessages" collection.
enum ValidationType{
NullCheck,
IsNumber,
RangeCheck,
....
}
class ErrorMessages{
const string FNEMPTY = "First Name is empty";
....
}
class BusinessObject{
function ValidateControl(Control cntrl, ValidationType type, object[] args, string message)
{
if(cntrl is TextBox)
{
if(cntrl as TextBox).Text.Trim() == String.Empty
errorMessages.Add(message);
}
...
}
}
Now you can call the above function on every control you want to validate. One more improvement will be to Implement a base class for all your controls that holds the corresponding error message collection in a property (Dictionary<ValidationType,String>). Then you can modify the validation function to accept an array of ValidationType enum to perform several validation in one call. Each control will give you its error message for every validation type in its property we created in the base class.
Hope this helps.
Thanks
Ven
I am trying to work around the fact that when they wrote asp.net MVC 3 they forgot to include code to add the unobtrusive validation attributes to select lists and their "fix" for this is to include it in MVC 4, which is no bloody use to anyone using MVC 3.
My proposed work around is to use Html.GetUnobtrusiveValidationAttributes() to add them myself, just like any other custom attributes, but i can't work out the correct syntax for calling the method. There are 2 overloads, one takes a string and the other takes a string and a ModelMetaData class. I understand the metadata param, I presume I just pass in ViewData.ModelMetadata but what should the string be? The MSDN documentation says it is "the specified HTML name attribute" which makes no sense to me. The HTML name attribute of what? The select list? Why would it need that and how does that help it know what property on my model i want the validation for? Looking at examples of usage they all seem to pass in the name of the property on my model that i want the validation attributes for, which makes sense. Unfortunately I can't get the method to return anything but an empty collection no matter what i pass in.
My model class is called Event and my property is called EventTypeID. I am using a slightly different viewmodel class as the basis for the view because i need to display a list of Events and also also allow a new event to be entered on the same view so i have a simple viewmodel class as below:
public class EventViewModel
{
public Model.Event NewEvent { get; set; }
public IEnumerable<Model.Event> Events { get; set; }
}
The dropdown list is mapped to the property like: #Html.DropDownListFor(model => model.NewEvent.EventTypeID what do I pass as the string to Html.GetUnobtrusiveValidationAttributes(string) or Html.GetUnobtrusiveValidationAttributes(string, ModelMetadata) to get the attributes for this property. I have tried:
Html.GetUnobtrusiveValidationAttributes("EventTypeID")
Html.GetUnobtrusiveValidationAttributes("EventTypeID",ViewData.ModelMetadata)
Html.GetUnobtrusiveValidationAttributes("NewEvent.EventTypeID")
Html.GetUnobtrusiveValidationAttributes("NewEvent.EventTypeID",ModelMetadata)
They all return an empty collection.
I know that my model is correct because if i change the call from Html.DropDownListFor to Html.TextBoxFor then the validation "just works" without me having to do anything other than add the validation attributes to my model class.
EDIT:
Just tried turning client side validation off, the validation works fine server side for all select lists.
For those still looking for an answer, this works for me:
public static IDictionary<string, object> UnobtrusiveValidationAttributesFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression)
{
var propertyName = html.NameFor(propertyExpression).ToString();
var metadata = ModelMetadata.FromLambdaExpression(propertyExpression, html.ViewData);
var attributes = html.GetUnobtrusiveValidationAttributes(propertyName, metadata);
return attributes;
}
Note that I'm using .Net MVC 4, you don't have the html.NameFor method in MVC 3. However, I believe this can be done in MVC 3 with the following method:
var propertyName = ExpressionHelper.GetExpressionText(propertyExpression);
You can use it inline
Example for select element
<select name="#Html.NameFor(m=> m.MyProperty)"
id="#Html.IdFor(m=> m.MyProperty)"
#Html.Raw(string.Join(" ", Html.GetUnobtrusiveValidationAttributes(Html.NameFor(m => m.MyProperty).ToString()).Select(x => x.Key.ToString() + "=\"" + x.Value + "\"")))
>
Here is a link to an answer I posted, showing an HtmlHelper I wrote to provide unobtrusive validation for dropdownlists: MVC 3 dropdownlist validation not working for complex view model
UPDATE
Are you trying to get the attributes in an HtmlHelper, or in-line in your view?
Assuming you are trying to get the attributes in your view, that is the problem.
First, you need to understand that ModelMetadata does not represent a single object available across your entire model. Rather, it represents the metadata for a particular element, be it your model, or any property within the model. A better descriptive name would be ObjectMetadata, since ModelMetadata is the metadata for a specified object, be it a model, a nested model, or a specific property.
ModelMetadata in the view is only the metadata for the top-level model. You must get the ModelMetadata for the property to which the dropdownlist is bound. If you use a helper, then the helper is passed the correct ModelMetadata as a matter of course. If you use your view, you need to engage in some gymnastics to get the correct ModelMetadata, see for example my answer here: Validating and editing a “Changeable”/optional type in Asp.net MVC 3