我有这个型号:
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
我建议使用fluentvalidation,因为它允许分离和重用验证规则。
在您的情况下,假设startdate
是CalendarAvailabilityRequest
的一部分,您将为请求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);
}
但是我建议你在验证输入时避免尝试捕获,除非你真的需要。
我最终写了一个实现Attribute
的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)
{
}
}
我将该属性应用于我的action方法:
[DateTimeResourceFilter]
[Route("{providerName}/CalendarAvailability")]
[HttpGet]
public Task<CalendarAvailabilityResponse> GetCalendarAvailability(CalendarAvailabilityRequest request)
{
return null;
}