У меня есть эта модель:
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
Вы можете ограничить маршрут ожидаемым форматом Такие как
Эти предложения кажутся хорошими, но работают только в том случае, если тип — строка. Я надеялся сохранить тип DateTime, если бы мог.





Я бы предложил использовать плавная проверка, поскольку он позволяет разделять и повторно использовать правила проверки.
В вашем случае, предполагая, что 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);
}
Но я бы рекомендовал избегать try catch, когда вы можете проверить ввод, если вам это действительно не нужно.
Разве это не сработает, только если StartDate имеет тип String? В моей модели StartDate имеет тип DateTime, поэтому к тому времени, когда он достигает конечной точки, он уже пытался преобразовать ввод в DateTime.
Правильно, это будет работать только для строк, и это способ внешнего описания вашего API, поскольку вы подразумеваете формат «гггг-мм-дд». Если вы разрешаете формат даты и времени, вы можете только проверить, что часть времени не установлена или используется по умолчанию, каким бы ни было значение по умолчанию. В таком случае, почему вы применяете этот формат, вы можете просто использовать часть DateTime.Date и игнорировать время, чтобы быть удобным для потребителя.
В итоге я написал 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)
{
}
}
Я применил атрибут к моему методу действия:
[DateTimeResourceFilter]
[Route("{providerName}/CalendarAvailability")]
[HttpGet]
public Task<CalendarAvailabilityResponse> GetCalendarAvailability(CalendarAvailabilityRequest request)
{
return null;
}
Я думаю, это поможет вам. stackoverflow.com/a/5392214/2845389. Используйте try parse точно в вашем случае.