ASP.NET Core MVC: ModelState value - c#

I am working on an ASP.NET Core MVC project. Currently, I'm trying to add actors from Create.cshtml view. The view has three fields and all of them are required:
ProfilePictureURL
FullName
Bio
I'm making these fields required with some data annotation attributes in the model.
In the controller, I am using [Bind] data annotation in the parentheses of the respective action method. Inside of its body I'm checking !ModelState.IsValid which is expected to be false. On the contrary, it's true and I don't get it why.
Here are some screenshots of model, controller and view.
Controller:
Model:
View:
Can anyone tell me why my !IsModelState.IsValid is always true?
Thanks in advance 😊

Inside of its body I'm checking !ModelState.IsValid which is expected
to be false. On the contrary, it's true and I don't get it why.
Altough, you haven't shared your code snippet in correct way and your asp.net core version information as well. However, if you use older version than asp.net core 6 in that case you might encounter this issue. Because, in older version of asp.net core when you use DataAnnotations in that scenario the property without any annotation doesn't take into account. In your case
public List<Movie> Movies { get; set; } has no DataAnnotations which would comletely be ignored in older than asp.net core 6.
Debug in asp.net core 6:
Note: As you can see in asp.net core 6 as you haven't use any DataAnnotations on Movies property therefore, by default it will consider as [Required] and finally !ModelState.IsValid will be always false. You can check here
Debug in asp.net core Older: 3.XXX:
Note: Here !ModelState.IsValid always true because in older version it will consider as nun-nullable and always consider true. You can check here
Solution:
You should redefine your public List<Movie> Movies { get; set; } and tell what kind of behavior should be expected here. But in dot net core 6 or higher version, it will autometically consider as false not matter you use dataAnnotation

Related

Array creates validation error in ASP.NET 6

I noticed a validation error when changing my WebApi project to use .NET 6 instead of .NET Core 3.1 for my array query parameter types. Previously they didn't return a validation error, but with 6.0 they do.
I have the following class for a query:
public class Query
{
public List<string> Id { get; set; }
}
And a controller with the following endpoint:
[HttpGet()]
public IActionResult Get([FromQuery] Test2 test)
{
return Ok()
}
When I send a query without any query parameters, I get a BadRequest marking the field id as required, when implemented using .NET 6. When using .NET Core 3.1 (or 5.0), the method executes correctly.
For reproduction I have created an ASP.NET Core WebApi project both with .NET Core 3.1, 5.0 and 6.0 and added a controller endpoint with a Query-Entity containing a string array as in the example above without any other changes.
Frankly I'm a bit stumped with this one. I tried to execute the Validator for the System.ComponentModel manually, but this yielded in the expected result (no error). I didn't add anything to the scaffold which would account for this behavior, it's basically identical. I didn't find any mention of this in the list of breaking changes for .NET 6.0 as well.
Adding a custom Validation Attribute ("NotRequired") to the Id property fixes the behavior, I would however prefer to be able to keep the query model as-is as it worked previously and the ComponentModel doesn't show any errors.
From the docs:
The validation system treats non-nullable parameters or bound properties as if they had a [Required(AllowEmptyStrings = true)] attribute. By enabling Nullable contexts, MVC implicitly starts validating non-nullable properties or parameters as if they had been attributed with the [Required(AllowEmptyStrings = true)] attribute.
You can make the property to be nullable reference type so it will make Id optional:
public class Query
{
public List<string>? Id { get; set; }
}
Or you can completely disable nullable context analysis which will also revert to the old behaviour (so personally I would recommend against it).

Model Binder null on List<object> sometimes

I have a view model,and one of the properties of the view model is a an object called Profile. One of the properties of Profile is a list of another object, called CD. From the view, I set the POST body values to the following
Profile.CD[0].Prop1=TEST&Profile.CD[0].Prop2=TEST&Profile.CD[1].Prop1=TEST2&Profile.CD[1].Prop2=TEST2
If I were to add a third object to the list in the view, which would get posted as Profile.CD[2].Prop1=TEST3 , in the controller all of the sudden Profile.CD is null. 2 items and below Profile.CD gets the values I would expect. As soon as I add in that third item the model binder stops working. I'm at my wits end, and I've tried everything I can think of.
Things I've tried
Removing an item from the view, and adding a new -- WORKS
Removing both from the view, and adding 2 new items -- WORKS
Adding a third item in the view -- FAILS Profile.CD is null in the view model
I'm not using any model state validation rules. When debugging, I've tried something like the following in the immediate window ?Request.Form.Where(x => x.Keys.Contain("Profile.CD")).ToList()) and sure enough, all of my values are present in the Request object even though the list is null in the view model.
The values of the objects in Profile.CD do not have to be unique.. I've tried setting every single value to "TEST" just to verify it's not the input causing this problem.
I'm truly lost..
View Model
public class PortalViewModel {
public ProfileModel Profile { get; set; }
}
Profile Model
public class ProfileModel {
//bunch of other static properties that are bound just fine.. like strings and decimals...
public List<CDModel> CD { get; set; }
}
Controller
public async Task<IActionResult> Edit (PortalViewModel Model)
{
Repo.UpdateProfile(Model.Profile); // in this method it foreachs Profile.CD , but sometimes this is null and other times it get's it's values....
return Ok();
}
I have not been able to find a coding solution to this problem, and started exploring other avenues. I have fixed the problem, but it makes very little sense.
I upgraded the project from .net core 1.0.1 to .net core 1.0.5 , and everything is working 100% of the time. In this upgrade, I made no code changes what so ever. Just upgraded .net core. Very very odd..

WebAPI 2 & Fluent validation - leverage RuleSet

I'm working on a Web API application, leveraging FluentValidation and AutoMapper and AutoFac. I have the basic setup, and everything is working as supposed. Validation is executed transparently for me, and in controller I can just check whether its valid or not:
[Route("")]
[HttpPost]
public HttpResponseMessage PostDinnerList(DinnerListDTO newDinnerList)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
var dinnerList = _mapper.Map<DinnerList>(newDinnerList);
dinnerList.Owner.Token = _userService.GetUserToken();
dinnerList = _dinnerListRepository.InsertDinnerList(dinnerList);
var newDinnerListDto = _mapper.Map<DinnerListDTO>(dinnerList);
return Request.CreateResponse(newDinnerListDto);
}
This is OK when I pass the DTO and save it into database. However I would like to leverage the same validator with ruleset to differentiate between when new entry is created and when it's edited as different rules kick in.
I saw that in MVC version of FluentValidation, CustomizeValidatorAttribute is available, and rulesets can be selected with it. There is nothing like that in WebAPI version.
I'm thinking how can I tackle the issue. Ideally I would like to have two actions: new/update, and use the same validator and just indicate which ruleset should be used.
Workarounds that come to my mind:
On edit action use different DTO than on new action and create separate validator - but I feel like creating unnecessary/duplicated/boilerplate code.
instantiate validator in the action with the controller manually - based on a condition, indicate ruleset and invoke validation manually - but that counterfeits whole IoC setup and abstraction.
Please advise.
In case your entity for example has an auto generated Ids (identity), You can probably depend on the id of the DTO to check if your entity is new (no id set) or it is about to update (id will be set) and build your validation rules according to that using "When" or "Unless" methods.
Other option is your DTO will have a state which describe the action that you want to perform, so Added, Updated, Deleted, and Unchanged, and building the validation rules around that using "When" or "Unless" methods.
Hope this helps.

Override view in ASP.NET MVC site not working

I have a separate project in my solution that contains some Controllers and compiled views.
I use those controllers for base classes to other controllers in my MVC application and the views are compiled using RazorGenerator.
Lets say B is Base Controller with non abstract action method SomeAction that returns View("_MyView"). _MyView.cshtml is compiled using RazorGenerator.
Lets say controller A inherits B but doesn't override SomeAction.
I've tried to make another view "~/Views/A/_MyView.cshtml" to override the default one, but it doesn't work. My question is how can I accomplish this?
ADDITIONAL INFO
1) I know that the views by default are searched in that order in those paths
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
2) Putting "~/Views/Shared/_MyView.cshtml" does override the view, but not only for controller A, but also for every other controller that inherits
B
3) Overriding SomeAction to return base.SomeAction() doesn't work
UPDATE
I have found similar question here, but doing the suggestion nothing happened
RazorGenerator Issues
I have posted my own issue here
Thank you in advance!
So far my only workaround is to install RazorGenerator on the consumer app and to also set the view _MyView.cshtml as being RazorGenerated. RazorGenator then picks up the correct view.
Another note for other visitors is not to compound the wrong view confusion with the route going to the base controller instead of the the consumer controller. In solving this issue earlier to being able to figure out the actual wrong view was being served by the right controller as the OP and I have an issue with. I have code in my base application_start that removes route duplicates.
Anyone else hitting this issue you need to update the RazorGeneratorMvcStart.cs to set PreemptPhysicalFiles = false in the master project. By default this is not the case and the views in the master project with take priority:
var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly)
{
UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal,
PreemptPhysicalFiles = false
};
ViewEngines.Engines.Add(engine);
This file is App_Start\RazorGeneratorMvcStart.cs. It is also important to Add the engine rather than Insert it. The default is to insert at position 0.
Note: I hit this issue when updating NuGet packages, it seems that the file gets overritten, resetting this back to the default behaviour.

How to register a custom modelbinder in MVC4 RC WebApi

I recently updated from MVC4 beta to the RC and have encountered a small issue with a WebApi project. One of the first things I noticed is that the ServiceResolver was removed. Before it was removed I was using it to register a custom model binder provider in the following way:
IEnumerable<object> modelBinderProviderServices = GlobalConfiguration.Configuration.ServiceResolver.GetServices(typeof(ModelBinderProvider));
List<Object> services = new List<object>(modelBinderProviderServices) { new CustomDynamicObjectModelBinderProvider() };
GlobalConfiguration.Configuration.ServiceResolver.SetServices(typeof(ModelBinderProvider), services.ToArray());
The action which took advantage of this model binder provider has the following signature:
[HttpPut]
public void Put(CustomDynamicObject model)
I tried to replace the old code with the following but with no results:
GlobalConfiguration.Configuration.Services.Add(typeof(ModelBinderProvider), new CustomDynamicObjectModelBinderProvider());
When I tried to PUT data to the given action, the model provider's GetBinder method is not called and the model paramter is set to null. I was able to make the action use the wanted modelbinder using the ModelBinder attribute by changing the signature of the Acion/method to the following
[HttpPut]
public void Put([ModelBinder(typeof(CustomDynamicObjectModelBinderProvider))] CustomDynamicObject model)
While this works I really wouldn't like to have to use this syntax in all my controllers/actions.
I think I should mention that my model binder provider is inheriting from:
System.Web.Http.ModelBinding.ModelBinderProvider
I am saying this because I saw that there is another ModelBinderProvider class in the following namespace:
Microsoft.Web.Mvc.ModelBinding
To recap: How to register a custom modelbinder in MVC4 RC WebApi ?
The model binding rules in Web API have changed since the Beta. The new rules are described in Mike Stall's post. Model binding for complex types now only works if you explicitly add a ModelBinder attribute to the parameter.
The parameter binding mechanism has changed again since the Release Candidate, so you probably want to wait for RTM before making too many changes. There are a couple of other options that might work in the RC version - depending on what the source is of the data you are trying to bind (query string or request body).
If the source of your data is the request body then you can create a custom MediaTypeFormatter rather than a model binder:
If your data comes from the querystring and you want to avoid explicitly including a [ModelBinder] attribute on your parameters, then you may be able to use a combination of a custom TypeConverter and a custom IValueProvider.
In the RTM version (or current nightlies), you will be able to use a custom HttpParameterBinding if the other options don't work.

Categories

Resources