输入参数的自定义验证器
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);
}
}
我目前有这个控制器
/// <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);
}
}