Navigation Menu

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());
}
@markvincze
Copy link

Hi @dotnetchris,
I'm not sure I understand how MethodCache would work.
It's there to speed up getting the list of parameters, right? But when we call GetOrAdd, the expression method.GetParameters() is evaluated every time anyway, so we get no improvement by having the cache, do we?

@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