how to populate data in controller and avoid ModelState errors - asp.net-mvc

Maybe that's not exactly the solution i need, but this is what i want to do:
i have a company registration form, and each company needs an administrative user. an administrative user may manage multiple companies, so in the company registration form, you can choose an existing user from a dropdown.
the company view model looks something like this:
public class CompanyViewModel {
[Required]
public string Name { get; set; }
// other properties...
public UserViewModel Administrator { get; set; }
public IEnumerable<UserViewModel> AvailableUsers { get; set; }
}
and the user view model looks like this:
public class UserViewModel {
[Required]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
// other properties...
}
in the company registration view:
<div><input type="radiobutton" name="chooseuser" id="existing"/>Choose an Existing User:</div.
<div>#Html.DropDownListFor(m => m.Administrator.Id, Model.AvailableUsers.Select(u => new SelectListItem { Text = string.Format("{0} - {1} {2}", u.UserName, u.FirstName, u.LastName), Value = u.Id.ToString() }), "<Choose existing user>", new { id = "existingusers" })
</div>
<div><input type="radiobutton" name="chooseuser" id="createnew"/>Create a new User:</div>
<div><label>Username:</label> #Html.EditorFor(m => m.Administrator.UserName)</div>
Through javascript, based on radio button selection, the dropdown list is disabled and the new user form shown, or the new user form is hidden and the dropdown list is enabled.
The problem is in the controller Save action after you press save, ModelState.IsValid is false if an existing user is chosen and no data is filled in on the form. If the user chooses to enter a new user, validation succeeds.
What is the best way to handle this?
One option is to load all data for all users into data structures in javascript, and when the value changes on the existing user dropdown, the hidden "create new" form fields can be populated. But this seems lame since passwords would be sitting the html in plain text. i can get fancier and use ajax for a "create new" form and populate a user id on the original form once the new user is saved, but i'd like to keep it all in one form if possible.
Seems liked i'd ideally be able to load the existing user data from the db and populate the model state in the controller Save action, but writing this code manually (even using reflection) seems sloppy. It would be nice if there was a built in method to do this.
Any ideas?
Thanks.

That's a typical scenario which perfectly illustrates the limitations of declarative validation (a.k.a Data Annotations). In order to handle it you could write a custom validation attribute which will be applied to the CompanyViewModel instead of individual properties and will allow you to perform the validation logic based on which radio button the user choose (btw you will need a property on your view model which will represent the radio button selection). The problem with model validators is that you might have some hard time handling the error highlighting.
That's one of the reasons why I use FluentValidation.NET instead of Data Annotations. This allows me to have the validation logic away from the model and done in an imperative way. It also allows me to have conditional validators which apply based on the values of some properties on the view model (in this case this would be the radio button selection).

You may want to consider a custom Modelbinder.
Here's some sample code from my site - this is part of a checkout page for a shopping cart - the user can enter an address but for US StateCd is sent and for non US StateOrProvince is sent. So we look at the country and remove any model errors for the other property that doesn't apply.
I think this is very similar to what you're describing (that you have two scenarios that need different rules but you want to use the same model).
The important code here is bindingContext.ModelState.Remove(...)which removes the model state and allows IsValid to return true.
public class AddressModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
// get the address to validate
var address = (Address)bindingContext.Model;
// remove statecd for non-us
if (address.IsUSA)
{
address.StateOrProvince = string.IsNullOrEmpty(address.StateCd) ? null : CountryCache.GetStateName(address.StateCd);
bindingContext.ModelState.Remove(bindingContext.ModelName + ".StateOrProvince");
}
else
{
address.StateCd = null;
bindingContext.ModelState.Remove(bindingContext.ModelName + ".StateCd");
}
// update country
address.Country = CountryCache.GetCountry(address.CountryCode, true).Name;
// validate US zipcode
if (address.CountryCode == "US")
{
if (new Regex(#"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled).Match(address.ZipOrPostal ?? "").Success == false)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName + ".ZipOrPostal", "The value " + address.ZipOrPostal + " is not a valid zipcode");
}
}
// all other modelbinding attributes such as [Required] will be processed as normal
}
}
Note: You need to register this modelbinder in global.asax. The modelbinding component is smart enough to let you create differnt model binders for any part of your model if it contains different objects.
ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();
Hope this helps. I think this applies to your situation.

Related

Secure read only and disabled HTML helpers

I have to secure my HTML helpers like textboxfor in MVC in a way if user inspects or f12 and change the value I should take the original value or prompt with an error. Is there any way to achieve it except disabling f12 or inspect option through some jquery and keep my value in some storage in the controller level and get again after posting.
I understand these ways around but I want to secure Html Helpers at the Razor level maybe through some custom HTML helper? please suggest .
example:
<div class="form-group">
#Html.TextBoxFor(x => x.PersonNameAr, new { #class = "form-control", #readyonly="readyonly"}) OR
#Html.TextBoxFor(x => x.PersonNameAr, new { #class = "form-control", #disabled="disabled"})
As a developer who wants to make a secure website, you have to pay attention to some details:
If there is something that user is not eligible to see, even if it's in cookie, session, or hidden input, so don't leak it to them.
You can't prevent user to access it's own computer abilities. Google chrome or any other softwares that installed in user's computer is not what you should be able to access it and change it's properties without official permission from user, if you do it, you've hacked users software.
If there is some data that is important to you, and you have to access it from your front-end, and user is not eligible to access it, you have to save it somewhere that user does not have access to it; somewhere like your database table. But HTML tags, cookies, sessionStorage, localStorage,... . These are not good places to save important data, these are the best place to save weak data that not affect your site's functionality.
there is a endless list of attentions to say, but for now that's enough.
If you want to prevent user from overposting data there are multiple ways to do so:
Bind Attribute. You can use Bind attribute in your controller's action to include or exclude some kind of data that user can't pass to your action as model properties.
imagine you have User model as below:
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsActive { get; set; }
}
and this is your Edit action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(User user)
{
//save expense to database
}
And you don't want user to be able to change IsActive property.
if you leave your action like above, any clever user can add extra property to the form and post it to action. But using Bind method like below you can prevent this kind of attack:
public ActionResult Edit([Bind(Include = "UserName", Exclude = "IsActive")]User user)
{
}
You can use ViewModel. Using ViewModel for each view helps you keep things simple and don't loose a point even if you accidentally forgot to use Bind attribute. In this case you can request or retrieve specific properties, and not even prevent attacks, but saving some memory.
Server side checking. In the post methods you can check your input explicitly to make sure user input does not damage your program.

MVC 4 Client Side Field validation for a list of fields (instead of fields as members of a class)

I am a long time backend .Net developer who is new to web application development. I am using MVC 4, Razor, EF 5, and I have a basic understanding on how to make a routine DB driven MVC 4 site with these tools.
I need to create a custom form capability for a workflow scenario. I have the entire code-first schema designed and classes for different formfield types, and formvalue type.
The model that will be passed to a view will be a form class with a list of formvalues, which contain form field specifications. So the view will have to iterate through the form fields and dynamically choose what editors to use and so on.
My problem is that this prevents me from using any data annotation for client side validation. And every place I find advice on custom validation assumes (not surprisingly) that all the fields are members of a class. Whereas, in this case, all the fields are on a list.
My goal is to end up with validation messages on each field and a validation summary as you would normally have if the view was simply bound to an annotated class.
Any suggestions on how to approach this? Thanks in advance.
Imagine view logic that has the following:
#foreach (var fieldvalue in Model.FormFieldValues) {
// Logic that chooses the appropriate editor based on typeof(fieldvalue.FormField)
// Binds the editor to fieldvalue.Value
// Validation specs are contained in the Formfield obtained by fieldValue.FormField
// And my problem is setting up the validation without the aid of the normal method
// of data annotation or class level custom validation.
}
and the fieldvalue class looks like this:
public class FormFieldValue : EboEntity
{
public string Value { get; set; }
[Required]
public int FormFieldId { get; set; }
[Required]
public virtual FormField FormField { get; set; }
[Required]
public int FormId { get; set; }
[Required]
public virtual Form Form { get; set; }
}
And imagine that the FormField object has fields such as Required, Maxlength, and so on, depending on the kind of field it is (i. e. FormFieldText would be a subclass of FormField)
ANSWER:
Ok, you can tell I am new at MVC.
The answer to my question is that the specific editors take htmlAttributes which can control validation. So in the case where one of my formfields is a required textfield of stringlenth(10), I can invoke the editor as follows:
<div class="editor-field">
#Html.TextBoxFor(model => model.NoTexty, new {
required = true,
maxlength = 10,
placeholder = "Texty"})
#Html.ValidationMessageFor(model => model.NoTexty)
</div>
but in my case the htmlAddtributes won't be hard coded, but rather they will come from the fields in the formvalue.FormField object.
Sorry if I wasted anyone's time on this. But I will leave this here for any other newbies like me.

What does it mean when a viewmodel contains [required]?

My project has viewmodels labeled like this:
public class locViewModel {
[Required]
public string City { get; set; }
}
If the view does not set a value then how can I detect this? Is this how the [required] works? Also what other kind of tags can I add to fields in a viewModel?
That means that for validation purposes you can do numerous things. For instance, in a View you can have client validation enabled and the form will not submit unless the control that is populating that property has data entered into it.
With a property with the Required attribute, and a Html.ValidationMessageFor(m => m.City, "City is required") you can notify the user on the client-side that it is a required field.
Here is a Great Resource on unobtrusive validation, and an in depth explanation of what you are looking for.

MVC Partial Model Updates

I often find myself in the situation where I only want to present and edit some fields from my model. Let's say I have a model that represts an address, perhaps I just want the form to update the city and post code fields (bad example, but hopefully it explains the scenario).
I know of two methods:
1) Persist the unwanted fields in hidden input elements on the form, or...
2) Create a dedicated view model that just defines the fields I need.
I favour option #2, but I don't have a nice clean way of merging the data from the view model back into the 'real' model within the controller action. At the moment, I follow this approach...
1) Store the record I'd in a hidden field on the view model
2) When the page posts back, the controller retrieves the original record and I manually assign each field from the view model to the real model
3) Save the real model back to the data store.
This works, but it is quite a lot of work and very easy to miss an assignment/reassignment and I was wondering if anyone knew of another approach?
Use the System.ComponentModel.DataAnnotations.MetadataType.
Something like:
public class BaseClassOfProperties
{
public string Name { get; set; }
}
public interface INameViewableProperties
{
[Display(name = "Your Name")]
string Name { get; set; }
}
public interface INameHiddenProperties
{
//[scaffoldColumn(false)] this completely hides the fields
[UIHint("Hidden")] // i think...
string Name { get; set; }
}
[MetadataType(typeof(INameViewableProperties)]
public class NameViewAbleProperties : BaseClassOfProperties
{
}
[MetadataType(typeof(INameHiddenProperties)]
public class NameHiddenProperties : BaseClassOfProperties
{
}

Remote attribute in asp.net mvc – in some situations restricts our model

I’ve got an unexpected situation when using Remote Attribute in ASP.NET MVC3.
The model type I used:
using System;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
namespace dTweets.Models
{
// at first time, user should create his account with unique username
// as in twitter.com, user do
public class UserMetadata
{
[HiddenInput]
internal int Identity { get; set; }
[Remote("IsUserExist", "Account")] // at any HttpPost, username should
// be unique – not appropriate if
// updating/editing this model later
[Required(ErrorMessage = "username should be unique")]
public string UserName { get; set; } // user cannot change it, later
[DataType(DataType.Password)]
public string Password { get; set; } // user can also change password, later
[DataType(DataType.MultilineText)]
public string About { get; set; } // Optional field – user can edit it later
}
[MetadataType(typeof(UserMetadata))]
[Bind(Include="UserName, Password, About")]
public partial class User
{
}
}
Remote attribute validates user unique name at account creation time. But when later user wants to update/change his account, Remote attribute did not allow to update model if keeping user unique name the same one.
This is not appropriate result because rarely user changes their unique user name. They just change other fields like About field or password etc.
[Note: at account creation time, I want to check user unique name so I used Remote attribute here, but at later time when updating user account I no longer need Remote attribute]
I must remove Remote attribute for updating this model later.
I want to update/change this model without changing user unique name (remote attribute is applied to this unique name).
one way to do this is to send ID value of this record in AdditionalFields named parameter like
[Remote("IsUserExist", "Account",AdditionalFields = "Identity")]
and then you can check for uniqueness across all rows except the ones that belong to current user. and don't forget to change signature of IsUserEsists action result to receive Identity like
public ActionResutl IsUserExists(string UserName, int Identity)
{
}
Can't you just change server side validation method to something like:
public ActionResult IsUserExists(string userName)
{
if (!UserService.UserNameExists(userName) || (CurrentUser.UserName == userName))
{
return "Yeah. Is it valid.";
}
}
You have current user, because he is logged in. As long as user can only edit his data, this will work.
This is one place where buddy metadata falls short.
Edit/Add scenarios require their own view models. One size fits all scenario validation attributes only work in very trivial business CRUD apps. Add and Edit actions happen in totally different contexts and are only transiently related. This concept is very similar to the DDD bounded context idea.

Resources