我使用的是ASP.NET Core和FluentValidation。
当POST操作收到无效输入时,通常会重新呈现输入表单视图,并显示验证错误:
if (!ModelState.IsValid)
return View("nameOfViewRenderedByGetAction", model);
但我的验证实际上是通过FluentValidation在服务中执行的,它会抛出ValidationException
。我想在异常过滤器中处理它:
public class ValidationFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// only handle ValidationException
var ex = context.Exception as ValidationException;
if (ex == null) return;
// re-render get action's view, or redirect to get action
// ??
}
}
我被困在" ??"部分,因为Core已经更改了许多类型的签名,并且ExceptionContext
没有表现出使其工作所需的数据。
我该怎么做?
答案 0 :(得分:1)
从异常过滤器中,您可以通过设置上下文结果来呈现自定义视图。
public class ValidationFilterAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// only handle ValidationException
var ex = context.Exception as ValidationException;
if (ex == null) return;
// re-render get action's view, or redirect to get action
var result = new ViewResult { ViewName = "GetView" }
context.HttpContext.Response.Clear();
context.Result = result;
}
}
GetView
应该是您获取操作视图的名称。
示例异常过滤器,它使用自定义开发人员错误视图来显示有关异常的详细信息。
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IModelMetadataProvider _modelMetadataProvider;
public CustomExceptionFilterAttribute(
IHostingEnvironment hostingEnvironment,
IModelMetadataProvider modelMetadataProvider)
{
_hostingEnvironment = hostingEnvironment;
_modelMetadataProvider = modelMetadataProvider;
}
public override void OnException(ExceptionContext context)
{
if (!_hostingEnvironment.IsDevelopment())
{
// do nothing
return;
}
var result = new ViewResult {ViewName = "CustomError"};
result.ViewData = new ViewDataDictionary(_modelMetadataProvider,context.ModelState);
result.ViewData.Add("Exception", context.Exception);
// TODO: Pass additional detailed data via ViewData
context.Result = result;
}
}
请注意,上面的代码正在向视图发送上下文,模型状态和异常。
如果您需要的是自定义错误页面,请参阅ASP.NET Core Error Handling
通常,您不应使用异常过滤器将错误转为成功。如果您有这样的要求,请考虑使用动作过滤器。 话虽如此,出于某种原因,如果你仍然需要从异常过滤器重定向,这就是它的完成方式
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IHostingEnvironment _hostingEnvironment;
public CustomExceptionFilterAttribute(
IHostingEnvironment hostingEnvironment,
IModelMetadataProvider modelMetadataProvider)
{
_hostingEnvironment = hostingEnvironment;
}
public override void OnException(ExceptionContext context)
{
if (!_hostingEnvironment.IsDevelopment())
{
// do nothing
return;
}
var result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Home", action = "Error" }));
context.Result = result;
}
}
答案 1 :(得分:0)
答案来得有点晚,但是我有一个适用于完全相同的应用程序设计的可行解决方案。我使用ASP.NET Core 3.0和FluentValidation 8.x。
public class MvcValidationExceptionFilterAttribute : ExceptionFilterAttribute
{
private IModelMetadataProvider ModelMetadataProvider { get; }
public MvcValidationExceptionFilterAttribute(IModelMetadataProvider modelMetadataProvider)
{
ModelMetadataProvider = modelMetadataProvider;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Framework calls without null")]
public override void OnException(ExceptionContext context)
{
if (context.Exception is ValidationException ex)
{
var validationResult = new ValidationResult(ex.Errors);
validationResult.AddToModelState(context.ModelState, null);
context.Result = new ViewResult { ViewData = new ViewDataDictionary(ModelMetadataProvider, context.ModelState) };
context.ExceptionHandled = true;
}
}
}
由于此过滤器具有依赖项,因此我们不能直接使用Attribute,而应在Startup.cs中通过依赖项注入对其进行注册:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<MvcValidationExceptionFilterAttribute>();
要使用ExceptionFilter,请通过ServiceFilterAttribute对其进行应用:
[ServiceFilter(typeof(MvcValidationExceptionFilterAttribute))]
public class MyController : Controller
{
或在Startup.cs中全局应用它:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add<MvcValidationExceptionFilterAttribute>();
})