Peter
Peter

Reputation: 329

ASP.NET Core validation when request model is different to response model

Let's say I have a view model like this:

public class MyViewModel
{
    //...Other properties and methods...
    public MyEntity Entity { get; set;}
}

I use MyViewModel in my view.cshtml

@model MyViewModel

But the POST controller method binds to the MyEntity class, as that's all it needs for the operation.

[HttpPost]
public IActionResult Add(MyEntity entityDetails)
{
    if (ModelState.IsValid)
        //if valid
    
    return View(new MyViewModel { Entity = entityDetails });
}

The issue is when validating MyEntity, the ModelState contains property names local to that class, whereas the view uses the parent "MyViewModel" class. When I try to retrieve errors for the entity, it can't find any as the ModelState dictionary will contain keys like "Name" and not "Entity.Name".

<form asp-action="Add" method="post">
    <input type="text" id="name" name="Name" class="form-control" />
    <span asp-validation-for="Entity.Name" class="text-danger"></span>

Is there a way around this? It would be nice if I could specify the "postback" model as well as the "view" model. Or if there was a way to modify the ModelState to prefix the keys with "Entity.". Or to force the asp-validation-for="Name" code to ignore compilation errors (which happens because it can't find the property).

Just to note also, taking MyViewModel in the Add controller action is not ideal. I won't go into the details, but it would make things quite awkward on my view. I could probably do it, but I would like to consider other options.

Upvotes: 0

Views: 814

Answers (1)

David Liang
David Liang

Reputation: 21546

There are 2 issues in your code that caused the problem.

1. The name of model posted back to the controller

When you're posting back only partial of the view model, the name of the partial model as the parameter of the controller cannot be arbitrary. It has to match the name you declared inside the view model.

In your case, you called it Entity in your MyViewModel, so the parameter of the Add() method has to be named the same:

[HttpPost]
// public IActionResult Add(MyEntity entityDetails)
public IActionResult Add(MyEntity entity)
{
    ...
}

2. The name of the Entity name input

I don't get why you used tag helper on the validation but not on the input. The name attribute on the Entity name input should have been name="Entity.Name" instead of your hard-coded name="Name".

And you don't want to hard code it like that. That's what the tag helper is here for.

<form asp-action="Add" method="post">
    @*<input type="text" id="name" name="Name" class="form-control" />*@

    <input asp-for="Entity.Name" class="form-control" />
    <span asp-validation-for="Entity.Name" class="text-danger"></span>
...

If you can fix these two, you should be able to see the key Entity.Name in the ModelState:

enter image description here

Upvotes: 1

Related Questions