Я рассмотрел этот вопрос о параметрах запроса DateTimeOffset , но я явно хочу иметь возможность передавать DateTimeOffset в качестве атрибута маршрута, а не параметра запроса (вероятно, потребуется дополнительная маршрутизация после даты). Кроме того, я хочу маршрут, который не включает дату, например так:
[Route("api/Controller/Action/")]
[HttpGet]
public async Task<ActionResult> ControllerAction()
{
//blah
}
[Route("api/Controller/Action/{dateParam:DateTimeOffset}")]
[HttpGet]
public async Task<ActionResult> ControllerAction(DateTimeOffset dateParam)
{
//blah
}
Эти маршруты оба возвращают InvalidOperationException: The constraint reference 'DateTimeOffset' could not be resolved to a type. Register the constraint type with 'Microsoft.AspNetCore.Routing.RouteOptions.ConstraintMap'.
Чтобы попытаться это исправить, я добавил и зарегистрировал ограничение:
//Constraint
public class DateTimeOffsetConstraint : IRouteConstraint
{
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values,
RouteDirection routeDirection)
{
if (values.TryGetValue(routeKey, out var value) && value != null)
{
return value is DateTimeOffset;
}
return false;
}
}
//In startup configure services
services.Configure<RouteOptions>(opt => opt.ConstraintMap.Add("DateTimeOffsetConstraint", typeof(DateTimeOffsetConstraint)));
//In controller, altered second route to use constraint
[Route("api/Controller/Action/{dateParam:DateTimeOffsetConstraint}")]
[HttpGet]
public async Task<ActionResult> ControllerAction(DateTimeOffset dateParam)
{
//blah
}
После этого изменения вызов первого маршрута возвращает InvalidOperationException: Unable to resolve service for type 'System.DateTimeOffset'
, в то время как второй с DateTimeOffset (в надлежащем формате jul времени Зулу, например, 2019-10-02T05: 04: 18.070Z) возвращает404.
Следующие вопросы @Kirk Larkin ... Контроллер берет IDatePeriodRepository, это определено в другом проекте. Это в какой-то момент DateTimeOffset передается в конструктор
public interface IDatePeriodRepository
{
Task<int> GetDatePeriod();
Task<int> GetDatePeriod(DateTimeOffset date);
}
//Defined in seperate file
internal class DatePeriodRepository: IDatePeriodRepository
{
private readonly DateTimeOffset _dateCycleStart;
public DatePeriodRepository(DateTimeOffset dateCycleStart)
{
_dateCycleStart = dateCycleStart;
}
public Task<int> GetDatePeriod()
{
return GetDatePeriod(DateTimeOffset.Now);
}
public Task<int> GetDatePeriod(DateTimeOffset date)
{
var yearDiff = (date.Year - _billingCycleStart.Year) * 12;
var monthDiff = yearDiff + date.Month - _dateCycleStart.Month;
return Task.FromResult(monthDiff);
}
}
Это построено с расширением коллекции сервисов
public static class ServiceCollectionExtension
{
public static IServiceCollection AddDatePeriodRepository(this IServiceCollection services, Action<Options> configuration)
{
var options = new Options();
configuration(options);
services.AddSingleton(options);
services.Configure(configuration);
return services.AddScoped<IDatePeriodRepository, DatePeriodRepository>();
}
}
//Used in startup ConfigureServices
services.AddBillingPeriodRepository(opt =>
opt.BillingPeriodCycleStart = Configuration.GetValue<DateTimeOffset>("BillingPeriodCycleStart"));
Как я могу иметь DateTimeOffset в качестве атрибута маршрута?
Smudge202'a ответ сработал, но мне также пришлось изменить метод сопоставления constrinat следующим образом:
public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values,
RouteDirection routeDirection)
{
if (values.TryGetValue(routeKey, out var value) && value != null)
{
return DateTimeOffset.TryParse(value.ToString(), out _);
}
return false;
}