0

i'm stuck with a problem in a net standard 2.0 library and i really don't know how to approach it.

let's say i have a controller like this:

    [Route("{action}/{Id}")]
    [HttpPut]
    public void UpdateUser([FromBody] UserDTO user, long id )
    {
        _service.UpdateUser(user, id);
    }

easy, it works. now i want to add some custom validation. i know i can use System.ComponentModel.DataAnnotations on the DTO, but the message when there's a bad request is... bad.

i tried to use ValidationAttribute with a isValid,a FormatErrorMessage and add it to my controller like so

    [MyValidation]
    [Route("{action}/{Id}")]
    [HttpPut]
    public void UpdateUser([FromBody] UserDTO user, long id )
    {
        _service.UpdateUser(user, id);
    }

but it never reached the MyValidation code, it just skipped it.

is there a way i can do this so that i can do all the validation i want and check it against my db and do all the stuff i need?

how can i proceed?

all the guides i found are related to .core or .mvc, things i can't use cos i'm on a net standard library even a guide would be wonderful! thanks to anyone who wants to help :D

My Solution!

so, after lots of research i found out that... it's not possibile to do it. at least from what i understood/tried

i did a bit of a workaround because what i used can't be used on a net standard library, whatever, it seems net standard doesn't have this kind of stuff to work with.

i used the IActionFilter.

i created a class and inherited the filter and put in the two methods that it requires, OnActionExecuted and OnActionExecuting.

public class UserFilter : IActionFilter
    {
       public void OnActionExecuted(ActionExecutedContext context)
        {
           
        }

      public void OnActionExecuting(ActionExecutingContext context)
        {
        }
    }

to read what it's inside the payload you can use

    UserDTO model = new UserDTO();
    model = context.ActionArguments["user"] as UserDTO;

where user is the "name" of your payload. to know what it is, you can use the debugger and read what's inside context.actionArguments. remember to use the " " between that name. you have to cast it using the "as" keyword to your model. now you have access to your payload and you can do your own custom validator!!!

in my case i created a list of string that will contain the string errors, validate id and throw an error if the list has at least one element.

example :

     UserDTO model = new UserDTO();
     List<string> CustomError = new List<string>();
     model = context.ActionArguments["user"] as UserDTO;
     if (model.Age == 0) //dumb control, just for the sake of the example
        {
           CustomError.Add("age cannot be 0!")      
        }
     //other controls
     if (CustomError.Count > 0)
        {
            context.Result = new BadRequestObjectResult(CustomError);
        }

the last if is important! if the list you made has at least one element, it will throw a bad request (error 400) and you will get back a json with all the errors.

and on top of the controller you want to custom validate use

     [HttpPost]
     [TypeFilter(typeof(UserFilter))]
     public IValidation<> PostUser(UserDTO user)
       {
        //your controller logic
       }

hope it will help somebody in the future who got stucked like me, trying to custom validate data in a net standard, which it seems is impossible

3
  • Are you aware you can set your own ErrorMessage on data annotations? You can also add errors to the ModelState in the controller for server-side validation. Commented Apr 21, 2021 at 20:36
  • @Crowcoder yep, but i have to find a way to build it in a class Commented Apr 21, 2021 at 21:05
  • 1
    There's a library called FluentValidation, where you can define all sorts of rules on your model and it works like a charm. Check it out: fluentvalidation.net Commented Apr 25, 2021 at 17:25

1 Answer 1

3

You need to implement the ValidationAttribute class and then you can write your own logic and use as data annotation on your models.

Sample Implementation

public class ValidJoinDate : ValidationAttribute
{      
    protected override ValidationResult 
            IsValid(object value, ValidationContext validationContext)
    {   
        DateTime _dateJoin = Convert.ToDateTime(value); 
        if (_dateJoin < DateTime.Now)
        {
            return ValidationResult.Success;
        }
        else
        {
            return new ValidationResult
                ("Join date can not be greater than current date.");
        }
    }        
}

And use in this way in your model properties:

public class Customer
{
    
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}"
        , ApplyFormatInEditMode = true)]
    [ValidJoinDate(ErrorMessage=
        "Join Date can not be greater than current date")] 
    public DateTime JoinDate { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = 
        "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    [ValidLastDeliveryDate(ErrorMessage = 
        "Last Delivery Date can not be less than Join Date")]
    public DateTime LastDeliveryDate { get; set; }
   
}

Hope this solved your problem 👍

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.