基于 asp.net 核心 2.2 和 FluentValidation 的请求路由的条件验证
Conditional validation based on request route with asp.net core 2.2 and FluentValidation
所以基本上我用 FluentValidation 为我的 class 写了一个验证器,还有一个过滤器在我的 webAPI 项目中为我做验证任务,到目前为止没问题,但假设我的用户 class具有名字、姓氏、电子邮件、密码属性
我有两条路线(一条用于注册,另一条用于登录)
正如您可能已经注意到的,这些路线所需的属性有所不同。
因此,我真的需要为我的每个操作编写单独的验证吗?因为这会造成大量代码重复,并且很难 change.is 有任何方法可以仅添加基于条件的必需条件关于带有单一验证的请求 class?
有什么建议吗???
更好的做法是使用工厂模式进行验证,并使用操作过滤器来短路不良请求。您可以使用类似这样的方法验证任何操作参数(Headers、请求主体等。
public class TestValidationAttribute : Attribute, IActionFilter
{
private string _requestModelName;
public TestValidationAttribute(string requestModelName)
{
_requestModelName = requestModelName;
}
public void OnActionExecuting(ActionExecutingContext context)
{
// using Microsoft.Extensions.DependencyInjection;
var services = context.HttpContext.RequestServices;
var accessor = services.GetService<IHttpContextAccessor>();
var factory = services.GetService<ITestValidatorFactory>();
var tokens = accessor.HttpContext.GetRouteData().DataTokens;
if (!tokens.TryGetValue("RouteName", out var routeNameObj))
{
throw new Exception($"Action doesn't have a named route.");
}
var routeName = routeNameObj.ToString();
var validator = factory.Create(routeName);
if (!context.ActionArguments.TryGetValue(_requestModelName, out var model))
{
throw new Exception($"Action doesn't have argument named {_requestModelName}.");
}
TestModel test;
try
{
test = (TestModel) model;
}
catch (InvalidCastException)
{
throw new Exception($"Action argument can't be casted to {nameof(TestModel)}.");
}
var validation = validator.Validate(test);
if (!validation.Successful)
{
context.Result = new BadRequestObjectResult(validation.ResponseModel);
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
public class TestController : Controller
{
[HttpPost]
[Route("Test/{id}", Name = "TestGet")]
[TestValidation("model")]
public IActionResult Test(TestModel model)
{
return Ok();
}
}
public class ValidationResult
{
public bool Successful { get; }
public ResponseModel ResponseModel { get; }
}
public class TestModel
{
}
public interface ITestValidator
{
ValidationResult Validate(TestModel model);
}
public interface ITestValidatorFactory
{
ITestValidator Create(string routeName);
}
所以基本上我用 FluentValidation 为我的 class 写了一个验证器,还有一个过滤器在我的 webAPI 项目中为我做验证任务,到目前为止没问题,但假设我的用户 class具有名字、姓氏、电子邮件、密码属性 我有两条路线(一条用于注册,另一条用于登录) 正如您可能已经注意到的,这些路线所需的属性有所不同。
因此,我真的需要为我的每个操作编写单独的验证吗?因为这会造成大量代码重复,并且很难 change.is 有任何方法可以仅添加基于条件的必需条件关于带有单一验证的请求 class? 有什么建议吗???
更好的做法是使用工厂模式进行验证,并使用操作过滤器来短路不良请求。您可以使用类似这样的方法验证任何操作参数(Headers、请求主体等。
public class TestValidationAttribute : Attribute, IActionFilter
{
private string _requestModelName;
public TestValidationAttribute(string requestModelName)
{
_requestModelName = requestModelName;
}
public void OnActionExecuting(ActionExecutingContext context)
{
// using Microsoft.Extensions.DependencyInjection;
var services = context.HttpContext.RequestServices;
var accessor = services.GetService<IHttpContextAccessor>();
var factory = services.GetService<ITestValidatorFactory>();
var tokens = accessor.HttpContext.GetRouteData().DataTokens;
if (!tokens.TryGetValue("RouteName", out var routeNameObj))
{
throw new Exception($"Action doesn't have a named route.");
}
var routeName = routeNameObj.ToString();
var validator = factory.Create(routeName);
if (!context.ActionArguments.TryGetValue(_requestModelName, out var model))
{
throw new Exception($"Action doesn't have argument named {_requestModelName}.");
}
TestModel test;
try
{
test = (TestModel) model;
}
catch (InvalidCastException)
{
throw new Exception($"Action argument can't be casted to {nameof(TestModel)}.");
}
var validation = validator.Validate(test);
if (!validation.Successful)
{
context.Result = new BadRequestObjectResult(validation.ResponseModel);
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
public class TestController : Controller
{
[HttpPost]
[Route("Test/{id}", Name = "TestGet")]
[TestValidation("model")]
public IActionResult Test(TestModel model)
{
return Ok();
}
}
public class ValidationResult
{
public bool Successful { get; }
public ResponseModel ResponseModel { get; }
}
public class TestModel
{
}
public interface ITestValidator
{
ValidationResult Validate(TestModel model);
}
public interface ITestValidatorFactory
{
ITestValidator Create(string routeName);
}