0

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

1 Answers1

1

You could use a RegEx validation like so:

[RegularExpression("Ready|Started", ErrorMessage = "Invalid Status")]

Another (probably cleaner) solution would be to implement a custom validation attribute: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-6.0#custom-attributes

You would have to remove the code in your setter method. But that should be no problem since ASP will ensure that the validation will be run before the request reaches your controller.

musium
  • 2,942
  • 3
  • 34
  • 67