Skip to content

Instantly share code, notes, and snippets.

@dotnetchris
Last active June 27, 2019 09:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dotnetchris/f48bc02db9113d46161ad618d091f8d8 to your computer and use it in GitHub Desktop.
Save dotnetchris/f48bc02db9113d46161ad618d091f8d8 to your computer and use it in GitHub Desktop.
AspNetCore.Mvc action filter for ensuring validation of both model state and all action parameters as a precondition to a MVC action
/// <summary>
/// This action filter ensures ModelState.IsValid is true as a precondition to entry into the MVC action
/// </summary>
/// <remarks>
/// This class also validates all action parameters unlike the default behavior of ModelState.IsValid
/// </remarks>
/// <see cref="https://blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/" />
public class EnsureModelStateIsValid : ActionFilterAttribute
{
private static readonly ConcurrentDictionary<MethodInfo, ParameterInfo[]> MethodCache
= new ConcurrentDictionary<MethodInfo, ParameterInfo[]>();
public override void OnActionExecuting(ActionExecutingContext context)
{
ValidateParameters(context);
if (context.ModelState.IsValid) return;
context.Result = new BadRequestObjectResult(context.ModelState);
}
private void ValidateParameters(ActionExecutingContext context)
{
var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
if (descriptor == null) return;
foreach (var param in GetParameters(descriptor.MethodInfo))
{
object arg;
context.ActionArguments.TryGetValue(param.Name, out arg);
Validate(param, arg, context.ModelState);
}
}
private void Validate(ParameterInfo parameter, object argument, ModelStateDictionary modelState)
{
var paramAttrs = parameter.CustomAttributes.Where(x => typeof(ValidationAttribute).IsAssignableFrom(x.AttributeType));
foreach (var attr in paramAttrs)
{
var validationAttribute = parameter.GetCustomAttribute(attr.AttributeType) as ValidationAttribute;
if (validationAttribute == null) continue;
if (validationAttribute.IsValid(argument)) continue;
modelState.AddModelError(parameter.Name, validationAttribute.FormatErrorMessage(parameter.Name));
}
}
private static IEnumerable<ParameterInfo> GetParameters(MethodInfo method) => MethodCache.GetOrAdd(method, x => x.GetParameters());
}
@dotnetchris
Copy link
Author

@markvincze good catch that should be GetOrAdd(method, x => x.GetParameters())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment