Last active
June 27, 2019 09:09
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 expressionmethod.GetParameters()
is evaluated every time anyway, so we get no improvement by having the cache, do we?