【问题标题】:ASP.NET Core send parameter validation using filtersASP.NET Core 使用过滤器发送参数验证
【发布时间】:2020-11-02 08:12:33
【问题描述】:

我有控制器方法,它有 2 个输入参数:DateTime dFrom, dtTo

所以,现在我像这样检查间隔:

public async Task<ActionResult> GetValues([FromQuery] DateTime dtFrom, [FromQuery] DateTime dtTo)
{
    if (dtFrom > dtTo)
    {
        return BadRequest($"{nameof(dtFrom)} < {nameof(dtTo)}");
    }
}

但是,我想像这样进行验证:

[DateFilterAttribute(dtFrom,dtTo)]
public async Task<ActionResult> GetValues([FromQuery] DateTime dtFrom, [FromQuery] DateTime dtTo)
{
    if (dtFrom > dtTo)
    {
        return BadRequest($"{nameof(dtFrom)} < {nameof(dtTo)}");
    }
}

DateFilterAttribute:

 public class DateFilterAttribute : ActionFilterAttribute
 {
    private DateTime _fromDt;
    private DateTime _dtTo;
    public DateFilterAttribute(DateTime fromDt, DateTime dtTo)
    {
        _fromDt = fromDt;
        _dtTo = dtTo;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {            
        context.HttpContext.Response.StatusCode = 400;
        var errorStr = $"{nameof(_fromDt)} < {nameof(_dtTo)}";
        context.HttpContext.Response.Headers.Add("Error", new string[] { errorStr });
    }
  }

但是,我不能在属性中使用输入参数。 那么,我可以这样做吗?这是正确的方式吗?

【问题讨论】:

  • 为什么不创建具有dtFromdtTo 属性的输入合同并实施IValidatableObject 进行日期验证?然后会自动处理400个状态码

标签: c# asp.net-core attributes action-filter


【解决方案1】:

我们可以使用ActionArguments 属性,它包含我们操作中使用的每个参数的条目。

public class DateRangeAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var fromKey = "fromDate";
        var toKey = "toDate";

        if (!context.ActionArguments.ContainsKey(fromKey) ||
            !context.ActionArguments.ContainsKey(toKey))
        {
            context.Result = new BadRequestResult();
            return;
        }

        if (!(context.ActionArguments[fromKey] is DateTime fromDate))
        {
            context.Result = new BadRequestResult();
            return;
        }

        if (!(context.ActionArguments[toKey] is DateTime toDate))
        {
            context.Result = new BadRequestResult();
            return;
        }

        if (fromDate > toDate)
        {
            context.Result = new BadRequestObjectResult(new
            {
                Error = "Bad date range"
            });
            return;
        }

        base.OnActionExecuting(context);
    }
}

在我们的行动中

[DateRangeAttribute]
public IActionResult Index(DateTime fromDate, DateTime toDate)
{
    return View();
}

【讨论】:

    【解决方案2】:

    对于在查询字符串中传递的参数,您可以直接使用HttpContext.Request.Query,因此您无需将任何内容传递给您的DateFilterAttribute 构造函数:

    public class DateFilterAttribute : ActionFilterAttribute
    {
        private DateTime _fromDt;
        private DateTime _dtTo;
    
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            _fromDt = Convert.ToDateTime(context.HttpContext.Request.Query["dtFrom"]);
            _dtTo = Convert.ToDateTime(context.HttpContext.Request.Query["dtTo"]);
    
            context.HttpContext.Response.StatusCode = 400;
            var errorStr = $"{nameof(_fromDt)} < {nameof(_dtTo)}";
            context.HttpContext.Response.Headers.Add("Error", new string[] { errorStr });
        }
    }
    

    现在你的GetValues 应该是这样的:

    [DateFilterAttribute]
    public async Task<ActionResult> GetValues([FromQuery] DateTime dtFrom,
                                              [FromQuery] DateTime dtTo)
    {
        //The rest of the code
    }
    

    【讨论】: