Принимать только определенный формат DateTime в методе действия

У меня есть эта модель:

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

Я думаю, это поможет вам. stackoverflow.com/a/5392214/2845389. Используйте try parse точно в вашем случае.

Kaushik 17.04.2019 07:01

Вы можете ограничить маршрут ожидаемым форматом Такие как

lloyd 17.04.2019 07:27

Эти предложения кажутся хорошими, но работают только в том случае, если тип — строка. Я надеялся сохранить тип DateTime, если бы мог.

David Klempfner 18.04.2019 03:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
236
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я бы предложил использовать плавная проверка, поскольку он позволяет разделять и повторно использовать правила проверки.

В вашем случае, предполагая, что 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.

David Klempfner 17.04.2019 13:20

Правильно, это будет работать только для строк, и это способ внешнего описания вашего API, поскольку вы подразумеваете формат «гггг-мм-дд». Если вы разрешаете формат даты и времени, вы можете только проверить, что часть времени не установлена ​​или используется по умолчанию, каким бы ни было значение по умолчанию. В таком случае, почему вы применяете этот формат, вы можете просто использовать часть DateTime.Date и игнорировать время, чтобы быть удобным для потребителя.

Raul 17.04.2019 14:05
Ответ принят как подходящий

В итоге я написал 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;
    }

Другие вопросы по теме