I need to return customized validation result (response) in validation attributes in ASP.Net core Web API

8.8k views Asked by At

I need to return customized validation result (response) invalidation attributes in ASP.Net core Web API This is the ValidationAttribute I have created.

class MaxResultsAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        int maxResults = (int)value;

        if (maxResults <= 0)
        {
            return new CustomValidationResult(new ValidationResult("MaxResults should be greater than 0"));
        }

        return ValidationResult.Success;
    }
}

I have created CustomValidationResult object inheriting ValidationResult so that I can return my own customized response:

public class CustomValidationResult : ValidationResult
{
    public int FaultCode { get; set; }

    public string FaultMessage { get; set; }

    public CustomValidationResult(ValidationResult validationResult) : base(validationResult)
    {
        FaultCode = 123;
        FaultMessage = validationResult.ErrorMessage;
    }
}

But it's not working I need to return my own error response

Actual Response:

    {
      "MaxResults": [
        "MaxResults should be greater than 0"
      ] 
    }

The response I'm expecting:

    {
      "code": "55",
      "message": "The following validation errors occurred: MaxResults should be greater than 0"
    }
1

There are 1 answers

0
Zhi Lv On BEST ANSWER

The response I'm expecting:

{
  "code": "55",
  "message": "The following validation errors occurred: MaxResults should be greater than 0"
}

I have reproduced the problem using your code, using the CustomValidationResult, I could only get the Actual Response like yours. To achieve above behavior, as a workaround, I suggest you could try to use the action filter to handle the Validation failure error response. Check the following sample code:

  1. Create custom ValidationError model which contains the returned fields:

     public class ValidationError
     {
         [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
         public string Field { get; }
    
         [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
         public int Code { get; set; }
    
         public string Message { get; }
    
         public ValidationError(string field,int code, string message)
         {
             Field = field != string.Empty ? field : null;
             Code = code != 0 ? code : 55;
             Message = message;
         }
     }
    
     public class ValidationResultModel
     {
         public string Message { get; }
    
         public List<ValidationError> Errors { get; }
    
         public ValidationResultModel(ModelStateDictionary modelState)
         {
             Message = "Validation Failed";
             Errors = modelState.Keys
                     .SelectMany(key => modelState[key].Errors.Select(x => new ValidationError(key,0, x.ErrorMessage)))
                     .ToList();
         }
     }
    
  2. Create custom IActionResult. By default, when display the validation error, it will return BadRequestObjectResult and the HTTP status code is 400. Here we could change the Http Status code.

     public class ValidationFailedResult : ObjectResult
     {
         public ValidationFailedResult(ModelStateDictionary modelState)
             : base(new ValidationResultModel(modelState))
         {
             StatusCode = StatusCodes.Status422UnprocessableEntity; //change the http status code to 422.
         }
     }
    
  3. Create Custom Action Filter attribute:

     public class ValidateModelAttribute: ActionFilterAttribute
     { 
         public override void OnActionExecuting(ActionExecutingContext context)
         {
             if (!context.ModelState.IsValid)
             {
                 context.Result = new ValidationFailedResult(context.ModelState);
             }
         }
     }
    
  4. Change the default response type to SerializableError in Startup.ConfigureServices:

         services.AddControllers().ConfigureApiBehaviorOptions(options =>
         {
             options.InvalidModelStateResponseFactory = context =>
             {
                 var result = new ValidationFailedResult(context.ModelState);
    
                 // TODO: add `using System.Net.Mime;` to resolve MediaTypeNames
                 result.ContentTypes.Add(MediaTypeNames.Application.Json);
                 result.ContentTypes.Add(MediaTypeNames.Application.Xml);
    
                 return result;
             };
         });
    
  5. Add the custom action filter at the action method or controller.

     [HttpPost]
     [ValidateModel]
     public async Task<ActionResult<Student>> PostStudent(Student student)
     { 
        ...
     }
    
  6. Create a Student model with custom attribute validation (Min18Years):

     public class Student
     {
         [Key]
         public int Id { get; set; }
    
         [Required(ErrorMessage = "Please enter name")]
         public string Name { get; set; }
    
         [Required(ErrorMessage = "Please choose admission date.")]
         [Display(Name = "Admission Date")]
         [DataType(DataType.Date)] 
         public DateTime AdmissionDate { get; set; }
    
         [Display(Name = "Date of Birth")]
         [DataType(DataType.Date)]
         [Min18Years]
         public DateTime DateofBirth { get; set; }
     }
    
    
     public class Min18Years : ValidationAttribute
     {
         protected override ValidationResult IsValid(object value, ValidationContext validationContext)
         {
             var student = (Student)validationContext.ObjectInstance;
    
             if (student.DateofBirth == null)
                 return new ValidationResult("Date of Birth is required.");
    
             var age = DateTime.Today.Year - student.DateofBirth.Year;
    
             if (age <= 0)
             { 
                 return new ValidationResult("MaxResults should be greater than 0");
             } 
             return (age >= 18)
                 ? ValidationResult.Success
                 : new ValidationResult("Student should be at least 18 years old.");
         }
     }  
    

After running the application, the result like this:

enter image description here

Reference:

Handle Validation failure errors in ASP.NET Core web APIs

Handling validation responses for ASP.NET Core Web API