public static IActionResult ValidationError(this ControllerBase controller, ValidationException validationException)
        {
            controller.Response.Headers.Add(DtoHelper.PayloadTypeHeaderName, DtoHelper.ValidationErrorPayloadType);
            var error = WebApiHostHelper.CreateValidationErrorDto(validationException);

            return(controller.BadRequest(error));
        }
Beispiel #2
0
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            ValidationErrorDto validationError = null;

            // Verify that the model state is valid before running the argument value specific validators.
            if (!actionContext.ModelState.IsValid)
            {
                // Prepare the validation error response
                validationError = ValidationErrorDto.CreateStandard();

                foreach (var fieldState in actionContext.ModelState)
                {
                    // Make sure that all the property names are camel cased and remove all model prefixes
                    var fieldName = WebApiHostHelper.EnsurePropertyNameIsCamelCase(
                        Regex.Replace(fieldState.Key, @"^(.*?\.)(.*)", "$2"));

                    // Get only the first error, the rest will be skipped
                    var error = fieldState.Value.Errors.First();

                    // Create the error message
                    var errorMessage = "Unknown error.";
                    if (!string.IsNullOrEmpty(error.ErrorMessage))
                    {
                        errorMessage = error.ErrorMessage;
                    }
                    else if (!string.IsNullOrEmpty(error.Exception?.Message))
                    {
                        errorMessage = error.Exception.Message;
                    }

                    // Add the error to the response, with an empty error code, since this is an unspecific error
                    validationError.AddFailure(fieldName, null, errorMessage);
                }
            }

            // Validate all the arguments for the current action
            foreach (var argument in actionContext.ActionDescriptor./*GetParameters()*/ Parameters)
            {
                // Skip all arguments without a registered validator
                _validatorTypes.TryGetValue(argument.ParameterType, out var validatorType);
                if (validatorType == null)
                {
                    continue;
                }

                // Get the registered validator
                var validator = (IValidator)actionContext.HttpContext.RequestServices.GetService(validatorType);

                if (validator == null)
                {
                    continue; // could not resolve validator
                }

                // Inject the action arguments into the validator, so that they can be used in the validation
                // This is a "hack" to, amongst other, support unique validation on the update commands where the resource id is needed to exclude itself from the unique check.
                if (validator is IParameterValidator)
                {
                    ((IParameterValidator)validator).Parameters = actionContext.ActionArguments;
                }

                // Validate the argument
                var argumentValue = actionContext.ActionArguments[argument.Name];

                if (argumentValue == null)
                {
                    validationError = ValidationErrorDto.CreateStandard($"Argument '{argument.Name}' is null");
                    break;
                }

                var validationResult = validator.Validate(argumentValue);

                // Return if the argument value was valid
                if (validationResult.IsValid)
                {
                    continue;
                }

                // Create an validation error response, if it does not already exist
                if (validationError == null)
                {
                    validationError = ValidationErrorDto.CreateStandard();
                }

                // Add every field specific error to validation error response
                foreach (var validationFailure in validationResult.Errors)
                {
                    // Make sure that all the property names are camel cased
                    var propertyName = WebApiHostHelper.EnsurePropertyNameIsCamelCase(validationFailure.PropertyName);

                    // Only add the first validation message for a property
                    if (!validationError.Failures.ContainsKey(propertyName))
                    {
                        validationError.AddFailure(propertyName, validationFailure.ErrorCode, validationFailure.ErrorMessage);
                    }
                }
            }

            if (validationError != null)
            {
                actionContext.Result = new ContentResult
                {
                    StatusCode  = StatusCodes.Status400BadRequest,
                    ContentType = "application/json",
                    Content     = JsonConvert.SerializeObject(validationError),
                };

                // Set the action response to a 400 Bad Request, with the validation error response as content
                actionContext.HttpContext.Response.Headers.Add(DtoHelper.PayloadTypeHeaderName, DtoHelper.ValidationErrorPayloadType);
            }
        }