How to validate your .NET Core WebAPI model and return a 400 (BadRequest) response from your controller and test it with Moq
I know, this is not a new question, however, if you search online you will mostly find articles writing about the older .NET Framework. Here is my way of dealing with 400 (BadRequest) and 404 (NotFound) errors with the latest .NET Core 3.0 WebAPI methods.
Let us assume that you want to add some DataAnnotations to a model of your API, for example here we add a Range:
1
2
3
4
5
public class PersonModel
{
[Range(0, 120)]
public int Age { get; set; }
}
You now validate the model in the controller method of your application, before you process with the business logic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using Microsoft.AspNetCore.Mvc;
[Route("[controller]")]
public class PersonController : Controller
{
public async Task<ActionResult<PersonModel>> Save(
[FromBody] PersonModel model,
CancellationToken cancellationToken = default)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return await repository.SaveAsync(model, cancellationToken);
}
}
-
We use the ModelState.IsValid property to check for validation errors in the model. If this is the case, then we return a BadRequest response from the Microsoft.AspNetCore.Mvc.ControllerBase abstract class, which is the parent class of the Microsoft.AspNetCore.Mvc.Controller class.
-
We pass the ModelState object in the BadRequest-response, so that the caller of our service gets information about the properties that did not validate correctly.
-
We always use as return-type the ActionResult for wrapping the response in it. This way we are flexible and we can return our model in a success case or another response when things go wrong.
-
Check and use the other methods of ControllerBase for returning other Response-Codes like NotFound or NoContent.
Test the IsValid code with XUnit and Moq
We now want to test the if-case with a unit-test. We will use the XUnit and the Moq libraries for that.
1
2
3
4
5
6
7
8
9
10
[Fact]
public async Task PersonController_WhenSaveIsCalled_ThenBadRequestIsReturned()
{
personController.ModelState.AddModelError("Age", "Range Error");
ActionResult<PersonModel> result =
await personController.Save(new PersonModel { Age = 200 }, default);
Assert.Equal(400, (result.Result as ObjectResult)?.StatusCode);
}
-
We first have to artificially add the errors in the ModeState object, since when we unit-test our code, the automatically-validation of .NET core does not run.
-
We then add a wrong value into the property that will get validated.
-
We call the controller method and Assert for an integer 400. We have to cast the ActionResult.Result object to a Microsoft.AspNetCore.Mvc.ObjectResult and then check its StatusCode property.
Thats it. Using the same principle you can also test for 404 responses.