在操作方法中仅接受某些DateTime格式

时间:2019-04-17 04:58:29

标签: c# asp.net-core

我有这个模型:

public class CalendarAvailabilityRequest
{
    [Required]
    [FromQuery]        
    public DateTime StartDate { get; set; }
}

以及此控制器/操作方法:

[ApiController]
[Route("api/[controller]")]
public class AppointmentController : ControllerBase
{        
    [Route("{providerName}/CalendarAvailability")]
    [HttpGet]
    public Task<CalendarAvailabilityResponse> GetCalendarAvailability(CalendarAvailabilityRequest request)
    {
        return null;
    }
}

在到达端点时,如何确定仅接受"yyyy-MM-dd"

例如这将被接受:

https://example.org/api?StartDate=2019-04-17

但是这些会引发异常:

https://example.org/api?StartDate=2019-17-04

https://example.org/api?StartDate=17-04-2017

2 个答案:

答案 0 :(得分:1)

我建议您使用fluentvalidation,因为它可以分离并重复使用验证规则。

在您假设startdateCalendarAvailabilityRequest一部分的情况下,您将为请求dto添加一个验证器:

public class CalendarAvailabilityRequestValidator : 

AbstractValidator<CalendarAvailabilityRequest> 
{
  public CalendarAvailabilityRequestValidator() 
  {
    RuleFor(request => request.StartDate)
        .Must(BeAValidDateFormat).WithMessage("Date must follow the format: yyyy-mm-dd")
        .NotNull().WithMessage("A start date must be provided.");
  }

  // will only match yyyy-mm-dd
  private static bool BeAValidDateFormat(string date)
    => Regex.IsMatch(date, "2\d{3}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$", RegexOptions.Compiled);
}

在您的控制器内,实例化一个验证器,并使其验证:

[Route("{providerName}/CalendarAvailability")]
[HttpGet]
public Task<IActionResult> GetCalendarAvailability(CalendarAvailabilityRequest request)
{
    var validationResult = new CalendarAvailabilityRequestValidator().Validate(request);
    if (!validationResult.IsValid)
    {
        Log.Warning(validationResult.Errors.ToString());
        return BadRequest(validationResult.Errors);
    }
    var statDate = DateTime.ParseExact(request.StartDate, "yyyy-mm-dd", CultureInfo.InvariantCulture);
    //TODO: calendar availability logic
    return OK(); 
}

当然,您也可以从上方使用 regex 并在控制器中验证请求。

另一种选择是尝试使用DateTime.ParseExact这样的捕获:

try
{
    var statDate = DateTime.ParseExact(request.StartDate, "yyyy-mm-dd", CultureInfo.InvariantCulture);
}
catch(exception ex)
{
  Log.Warning("Request for {startdate} was invalid: {message}", request.StartDate, ex.Message);
  return BadRequest(ex.message);
}

但是我建议您在可以验证输入时避免尝试catch,除非您确实需要。

答案 1 :(得分:1)

我最终写了一个实现Attribute的{​​{1}}:

IResourceFilter

我将该属性应用于了我的操作方法:

public class DateTimeResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        const string PreferredDateTimeFormat = "yyyy-MM-dd";
        string dateTimeString = context.HttpContext.Request.Query["StartDate"].First();
        bool isPreferredDateTimeFormat = DateTime.TryParseExact(dateTimeString, PreferredDateTimeFormat, new CultureInfo("en-AU"), DateTimeStyles.None, out DateTime dateTime);
        if (!isPreferredDateTimeFormat)
        {
            context.Result = new ContentResult()
            {
                Content = $"Date must be in the following format: {PreferredDateTimeFormat}",
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}