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

问题描述 投票:1回答:2

我有这个型号:

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

c# asp.net-core
2个回答
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(); 
}

当然,您也可以使用上面的正则表达式并验证控制器的请求。

另一个选择是尝试使用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);
}

但是我建议你在验证输入时避免尝试捕获,除非你真的需要。


1
投票

我最终写了一个实现AttributeIResourceFilter

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)
    {
    }
}

我将该属性应用于我的action方法:

    [DateTimeResourceFilter]
    [Route("{providerName}/CalendarAvailability")]
    [HttpGet]
    public Task<CalendarAvailabilityResponse> GetCalendarAvailability(CalendarAvailabilityRequest request)
    {
        return null;
    }
© www.soinside.com 2019 - 2024. All rights reserved.