处理异常过滤器中的验证失败,并重新呈现视图

时间:2017-02-17 18:59:31

标签: c# validation asp.net-core unobtrusive-validation fluentvalidation

我使用的是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没有表现出使其工作所需的数据。

我该怎么做?

2 个答案:

答案 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>();
            })