I have a dto with a property where I only want to accept certain values, say ”Ready” or ”Started” and nothing else. I try to hinder any other values of being assigned to the property by placing some ”validation” inside the set-method, such as
public class Dto
{
private string? _status;
public string? Status
{
get => _status;
set
{
_status = !string.IsNullOrWhiteSpace(value) &&
(value == "Ready" ||
value == "Started")
? value
: throw new ArgumentException("Status value was not allowed. Value: " + value);
}
}
}
(One could use enums for ”Ready” and ”Started” of course)
Why would I want this?
-> the purpose of the dto is to act as a carrier of data between a controller and database table. In my experience, frontend- and backend-logic more often than not dependent on string-values in the database belonging to a set of values, especially in the case of strings.
Therefore, the goal here is to strictly enforce a string-property to only accept some particular values. By having this limitation, I achieve what I can see at least 2 things:
- Input to Apimethod in controllers won’t be able to be incorrect
- In the future when the rule of what values are accepted is forgotten, it hinders myself and other programmers from using the dto in an unsought manner (unless of course the ”validation” is altered)
However, If we agree on that it is bad practice to return exceptions instead of controlled http-responses in a controller, I imagined I could add IValidatableObject to the dto and make the same validation there, this produces a more concise error message in the response. The above class rewritten:
public class Dto : IValidatableObject
{
private string? _status;
public string? Status
{
get => _status;
set
{
_status = IsValidStatus(value)
? value
: throw new ArgumentException("Status value was not allowed. Value: " + value);
}
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (IsValidStatus(Status))
yield return new ValidationResult("Ticket status invalid");
}
private bool IsValidStatus(string input)
=> string.IsNullOrWhiteSpace(input) &&
(input== "Ready" ||
input== "Started");
}
If this was working as intendend, an incorrect status produces a response like
{
"type": "...",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "...",
"errors": {
"Status": [
"Value wasn't an accepted status."
]
}
}
The problem I have is that the Dto is instantiated before the validation is performed, thus producing a exception and 500-response instead
So I am at a loss at how to
- having Validation in my controller, and
- limit the Dto from accepting incorrectly set values.
Thank you