输入参数的自定义验证器

Custom validator for input parameter

我目前有这个控制器

/// <summary>
/// API methods for working with test
/// </summary>
[RoutePrefix("api/terminal")]
public class TerminalController : ApiController
{
    [HttpGet]
    [Route("{terminalId}/validation")]
    public IHttpActionResult ValidateTerminal([MinUnsignValue(10)] long terminalId)
    {
        return Ok();
    }
}

以及输入参数的自定义验证器

public class MinUnsignValueAttribute : ValidationAttribute
{
    private readonly ulong _minValue;

    public MinUnsignValueAttribute(ulong minValue)
    {
        _minValue = minValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        //if value < _minValue return new ValidationResult("false")

        return ValidationResult.Success;
    }
}

当我发送 6 时,验证程序被忽略并调用了操作。这是因为 web api pipeline 由 IActionFilter[] IAuthenticationFilter[] IAuthorizationFilter[] authorizationFilters IExceptionFilter[] 组成。

有没有办法将我的自定义属性集成到管道中?

好的,我没有找到答案。自己写的

public class ParametersFilter : ActionFilterAttribute
{
    class Wrapper
    {
        public Wrapper(MethodInfo methodInfo, object validationAttributeInstance)
        {
            MethodInfo = methodInfo;
            ValidationAttributeInstance = validationAttributeInstance;
        }

        public MethodInfo MethodInfo { get; }
        public object ValidationAttributeInstance { get; }
    }

    static readonly ConcurrentDictionary<string, Wrapper> Cache = new ConcurrentDictionary<string, Wrapper>();
    static readonly HashSet<string> Registry = new HashSet<string>();

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!Registry.Contains(actionContext.ActionDescriptor.ActionName))
        {
            Registry.Add(actionContext.ActionDescriptor.ActionName);

            //first invokation, have to check
            Type controllerType = actionContext.ControllerContext.Controller.GetType();
            foreach (MethodInfo methodInfo in controllerType.GetMethods())
            {
                if (actionContext.ActionDescriptor.ActionName != methodInfo.Name) continue;

                ParameterInfo[] parameters = methodInfo.GetParameters();
                if (parameters.Length != actionContext.ActionArguments.Count) continue;

                foreach (ParameterInfo parameterInfo in parameters)
                {
                    //The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
                    if (!parameterInfo.ParameterType.IsPrimitive) continue;
                    if (!actionContext.ActionArguments.ContainsKey(parameterInfo.Name)) continue;

                    var customAttributesData = parameterInfo.GetCustomAttributesData();
                    foreach (CustomAttributeData customAttributeData in customAttributesData)
                    {
                        if (!customAttributeData.AttributeType.IsSubclassOf(typeof(ValidationAttribute))) continue;

                        var validationAttributeInstance = customAttributeData.Constructor.Invoke(customAttributeData.ConstructorArguments.Select(x => x.Value).ToArray()) as ValidationAttribute;
                        if (validationAttributeInstance == null) continue;

                        MethodInfo method = validationAttributeInstance
                            .GetType()
                            .GetMethod("IsValid", BindingFlags.NonPublic | BindingFlags.Instance);

                        Cache.TryAdd(
                           $"{actionContext.ActionDescriptor.ActionName}{parameterInfo.Name}",
                           new Wrapper(method, validationAttributeInstance));
                    }
                }
            }
        }

        foreach (var actionArgument in actionContext.ActionArguments)
        {
            Wrapper wrapper;
            if (!Cache.TryGetValue(
                $"{actionContext.ActionDescriptor.ActionName}{actionArgument.Key}", out wrapper))
            {
                continue;
            }

            object instance = actionArgument.Value;
            var context = new ValidationContext(instance)
            {
                DisplayName = actionArgument.Key,
                MemberName = actionArgument.Key
            };

            var results = wrapper.MethodInfo.Invoke(wrapper.ValidationAttributeInstance, new[] { instance, context }) as ValidationResult;
            if (results != ValidationResult.Success)
            {
                var response = new Utils.WebApi.Common.Response.WebResponse
                {
                    IsSuccess = false,
                    ErrorMessage = results?.ErrorMessage
                };

                actionContext.Response = new HttpResponseMessage
                {
                    StatusCode = HttpStatusCode.BadRequest,
                    Content = new ObjectContent(
                        response.GetType(),
                        response,
                        new JsonMediaTypeFormatter())
                };
                break;
            }
        }

        base.OnActionExecuting(actionContext);
    }
}