I have a User class with multiple string properties, all of them required. The properties are used for different actions, like Create and Update.
In my form, on create action, i am using only a part of these properties, and, because of this, the ModelState is invalid.
Is there a way to specify to the ModelState that it should validate only the properties that are included in the POST data (inside the form) ? So the missing properties are ignored?
You can create different models for user creation and other actions with different sets of validation attributes.
Yea, you should create different view models for each specific action method ( if they have specific requirements ).
You can use the RequiredIf attribute from Foolproof to achieve this:
using Foolproof;
[RequiredIf("Tab", "Information")]
public bool UW_AgentCreditReportsAknowlegement { get; set; }
Just use one of the attributes and set the appropriate condition.
Related
I have the following model:-
[MetadataType(typeof(TMSServer_Validation))]
[Bind(Exclude = "TMSRack,ServerModel")]
public partial class TMSServer
{
}
and I have the following drop down inside my view:-
#Html.DropDownListFor(model =>model.Server.TMSRack.DataCenterID, ((IEnumerable<TMS.Models.DataCenter>)ViewBag.DataCenters).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.Name),
Value = option.ID.ToString(),
Selected = (Model.Server.TMSRack != null) && (option.ID == Model.Server.TMSRack.DataCenterID)
}), "Choose...")
Then on my controller class I have the following :-
ViewBag.Racks = repository.getrelatedracks(Server.TMSRack.DataCenterID);
But since I have excluded the TMSRack navigation property (mainly to avoid over-posting attacks), so the Server.TMSRack.DataCenterID will always be null. And to get its value I wrote the following:-
ViewBag.Racks = repository.getrelatedracks(Int32.Parse( Request.Form["Server.TMSRack.DataCenterID"]));
But I know that using Request.Form is not the right approach to follow, so my question is there a way to get the excluded property using more reliable way ?
Thanks
My answer is going to assume TMSServer is a domain model.
With that in mind, this is the perfect example of when to use a view model. By using a view model instead, you have complete control over how the properties are mapped from the view model to the domain model. Something like:
public class RackViewModel
{
public int DataCenterID
// other Rack properties
}
Then either send a list of RackViewModel to your view, or create a view model that encompasses all of that, too:
public class ContainerViewModel
{
public List<RackViewModel> Racks { get; set; }
// other view-specific properties
}
Now, when you POST the data back, not only do you have complete control over what properties you want to bind to your view models, you also have complete control over the mapping that takes place from converting your view models to domain models.
The bottom-line is this: if your view accepts a view model that only allows the user to POST the data they should be allowed to POST, over-posting doesn't even exist. Well-designed view models, or even making the distinction between a view model and an input model (i.e. a separate model that represents the data you want to bind back to in your action), eliminates over-posting entirely.
Over-posting only exists because you're not restricting the model binding process enough. If you ask it to bind to a class that has 10 properties in it when you only need 3 you're allowing the user to potentially stuff data into those other 7 properties.
This is one reason why view models are so popular. They allow you to narrow the scope of your view, whilst also narrowing the scope of the model binder. That leaves you free to properly manage the process of mapping from your view model to your domain model, without introducing a vulnerability.
Update
As you don't want to go the view model approach, your idea will work but you can do it slightly differently. Something along the lines of:
public ActionResult SomeAction(SomeModel model, TMSRack rack)
Where:
SomeModel is the type of model you're decorating with Bind(Exclude...) (it's not obvious what type that is from your question.
TMSRack is the type I assume you want to bind to.
As TMSRack is defined in your main model anyway, as long as you're using the Html.* helpers, it will have the correct names generated for it on the form in order to bind straight back to it as a separate parameter on your action. Then you can do whatever you want with it, without resorting to Request.Form.
I have a Model Like this
public int Id {get;set;}
[Required]
public string FirstName{get; set}
[Required]
public string LastName{get; set}
The Id is auto generate in DB. when I want call Create action The ModelState says that "The Id field is required"!!!! I Found this for my problem but it not clean solution.
Is there an other way to solve this?
Is there a way that I change Mvc ModelBinder behavior for value types?
The best solution to this problem is to use a view model. A view model is a class that you specifically design to meet the requirements of your view. So your controller action will take this view model as parameter. Simply stop passing your domain models to your views. That's it.
And if you don't want to follow good practices and use view models you could disable this implicit validation by adding the following to your Application_Start:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
another hack is to exclude this property from binding using the [Bind(Exclude = "Id")] attribute. Yeah, it's a hack but if you don't follow good practices you will have to live with hacks.
Although #DarinDimitrov suggested a good option to use View Models, In addition to that answer. Also, consider this option only when you don't want to create the View-Model
How about ModelState["Id"].Errors.Clear(); in your Post Action Method ?
You can also set the property type to Nullable<T>, and then just manually enforce any required validation you might need to do before working with the database.
public int? Id {get;set;}
Further Reading:
Unrequired property keeps getting data-val-required attribute
ASP.NET MVC optional field being treated as required
"The Id field is required" validation message on Create
Here's my situation: I've got a number of specialized object types in my application, and I'm following the standard convention for displaying them with custom templates in the /Shared/DisplayTemplates folder, and editing them with templates in /Shared/EditorTemplates. But I also want to be able to display a custom filter template for each type, so I'd like to add a /Shared/FilterTemplates folder, and implement my own #Html.FilterFor method, so that showing a Filter template is exactly like showing a Display or Editor template.
Does this seem like the best way to handle this situation, or is there a more correct/elegant way to do this in MVC? Thanks in advance.
I'm always using EditorTemplates when data is sent back to server. I assume the user can submit the filter to the server to perform the actual filtering.
When creating filters I prefer to create a model for the filter like:
public class UserListFilterModel
{
public string Username { get; set; }
public bool IsEnabled { get; set; }
}
The view for UserListFilterModel goes into EditorTemplates/UserListFilterModel.ascx.
And then add it as a property on my view model for the page.
public class MyPageViewModel
{
public UserListFilterModel Filter { get; set; }
}
Then I add the filter model to the model for the page and displays it like this:
<%= Html.EditorFor(x => x.Filter)%>
You are probably wrapping the filter in a form to allow the user to submit the values so I think it belongs in EditorTemplates. The users is in fact editing the filter model.
(If you really want to separate them ing you could use the UIHintAttribute but I wouldn't)
Edit: I added some sample code.
I think you misunderstand how Templates work. Templates do not make sense in the context you are describing.
Templates work on a SINGLE data item (although that data item can contain multiple data items, which in turn have their own templates).
The concept of a Filter is to control multiple data items, thus they do not map well to a template.
What you could do is create a DisplayTemplate for your collection class that adds filtering, thus no need to create a custom type of template. Just use DisplayTemplates.
I have a class set up to hold values on a registration form (VB.NET, MVC), and among the properties is a Password property:
Public Class RegisterModel
...
Private _password As String
<DisplayName("Password:"), Required(), ValidatePasswordLength(), DataType(DataType.Password)> _
Public Property Password() As String
Get
Return _password
End Get
Set(ByVal value As String)
_password = value
End Set
End Property
This works great when registering a new user, but I'd like to use the same class to update existing users. (Note: this app is run by an admin who is in charge of registering individuals and assigning passwords.) The way I'd like it to behave is if the admin leaves the password blank, then the password is not changed, but the rest of the information is. If I use this class, the password can't be left blank because it fails on the Required() and ValidatePasswordLength() calls.
Is there a way to use this class but tell the model to ignore these particular validations? Even if I leave the password field off my edit form, it still fails. Do I need to create a whole duplicate class without these restrictions on the password field? There must be a better way.
You could implement IDataErrorInfo and have a flag set on the model which indicates whether it is being used by an admin or not - you could then validate conditionally.
But overall, I'd say this is a bit of a code smell. You're using a model for two different, incompatible purposes. It'd be better to use a separate view model.
I'd recommend using the FluentValidation library. It's a fantastic way to separate the concerns of your view (view model) and the actual validation you want to perform. You could pass parameters into it to drive different behavior. Check out When/Unless conditions or just writing completely custom validation methods with the Must operator.
public class RegisterModelValidator: AbstractValidator<RegisterModel>
{
public RegisterModelValidator(bool isAdmin)
{
RuleFor(x => x.Password).NotEmpty().Unless(isAdmin);
...
}
}
As long as your view model would have identical properties in both scenarios, you should use the one view model and one validation class. If the model varies at all I'd use two view models as David recommends.
You can do this in 2 ways:
1: add the [ValidateInput(false )] attribute to the action
or
2: Add a new property to the Register Model
public bool IsNewUser {get;}
3: Create a new class level attribute that takes IsNewUser into account when validating
I have a simple object
public class SomeObject
{
public Int32 id { get; set; }
public string name { get; set; }
}
In a strongly typed view I am letting the user edit SomeObject.name, when the form is posted the receiving method doesn't see SomeObject.id in FormCollection (it does see SomeObject.name). Do I need to actually place every object property in the form to be able to access them when form is posted?
What's the best practice, should I just insert hidden fields for each property I don't plan letting the user edit? maybe I should place the entire object in the ViewData?
Thanks
FormCollection contains only properties that have been posted either through text fields or hidden fields. So if you need to use the Id property in your controller action you need to include it in your form. Depending on what you are doing in your controller action you might or might not include it. It is not necessary to include hidden fields for each property.
Usually the Id is sufficient because it allows you to later retrieve the object from your data store given this id.
What does your action method look like to which you are posting the form? If your method to handle the GET requests takes id, if your POST method also takes id and you are using the BeginForm helper method with none of the parameters overloaded, the form method will take the id as a parameter and you won't need to worry about including hidden fields for the id itself.