Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating DTOs with ModelState

I'm creating .NET Core WEB API project together with Entity Framework using Code First approach. I have troubles validating input from the requests, as ModelState validation is always true.

My application consists of 3 layers.

  • Data Access Layer
  • Business Logic Layer
  • .NET Core API

Example DataModel in DAL:

public class Group
{
    [Key]
    [Required]
    public long GroupId { get; set; }
    [Required]
    public string Name { get; set; }
    [Required(AllowEmptyStrings = false)]
    public string Description { get; set; }
    public DateTime CreationDate { get; set; }
    public bool IsActive { get; set; }
}

Corresponding DTO:

public class GroupDto
{
    public long GroupId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

Controller method:

[HttpPost]
public IActionResult Post([FromBody] GroupDto groupDto)
{
    Group group = _mapper.Map<Group>(groupDto);
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    _groupService.Add(group);

    groupDto = _mapper.Map<GroupDto>(group);

     return Ok(groupDto);
}

As far as I understand in current state ModelState.IsValid would always return true as GroupDto does not have any validations done via DataAnnotations.

How the DTOs should be validated? I'd like to avoid repeating the same validations in two places. Should additional custom DtoValidator be created or am I missing somtething and there is way to perform those validations.

like image 547
banneh Avatar asked Sep 18 '26 10:09

banneh


1 Answers

Model state validation will occur on the model being passed in, which in your case is GroupDto. Just because you eventually map to the Group class has no bearing on how the validation works. You will need to repeat the validation attributes in the DTO. This does duplicate code, but also allows you to customize the rules, since you may or may not want the exact same set in the DTO. An example of this is your primary key. For creation (POST) you don't necessarily want the GroupId to be a required field to pass in to the controller since the DB may be auto-generating that field (depending on your setup).

If you are using ASP.Net Core 2.1 or later, you can also now apply the [ApiController] attribute to the controller class and it will automatically apply the model validation rules. This eliminated the need to manually check for ModelState.IsValid. The system will automatically return a 400 Bad Request if the model is invalid. (https://learn.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-2.2#annotation-with-apicontroller-attribute)

like image 61
Bryan Lewis Avatar answered Sep 20 '26 23:09

Bryan Lewis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!