Настраиваемый DateTime HttpParameterBindings приводит к нахождению нескольких действий
У меня есть несколько пользовательских HttpParameterBindings, которые я пытаюсь установить в своем API. Созданные мной привязки работают индивидуально, но я не могу заставить их работать вместе. Если у меня есть только DateTime
связывание, тогда он будет ловить только параметры, которые являются DateTimes. Если у меня есть только DateTime?
При связывании он будет перехватывать только те параметры, которые могут иметь значение NULL (необязательно). Это небольшой кусочек большого существующего API, который будет сложно сделать серьезным рефакторингом.
В моем WebApiConfig.Register()
У меня есть следующее:
...
config.ParameterBindingRules.Add(typeof(DateTime?), GetNullableDateTimeBinding);
config.ParameterBindingRules.Add(typeof(DateTime), GetDateTimeBinding);
/*
I also tried this but it also failed due to the multiple actions issue
config.ParameterBindingRules.Insert(0, parameter =>
{
if (parameter.ParameterType == typeof(DateTime)
|| parameter.ParameterType == typeof(DateTime?))
return new DateTimeParameterBinding(parameter);
else
return null; // default to any other rules for datetimes that may exist
});
*/
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
...
Методы, которые они используют, следующие:
private static HttpParameterBinding GetNullableDateTimeBinding(HttpParameterDescriptor descriptor)
{
return (descriptor.ParameterType == typeof(DateTime?))
? new NullableDateTimeParameterBinding(descriptor)
: null;
}
private static HttpParameterBinding GetDateTimeBinding(HttpParameterDescriptor descriptor)
{
return (descriptor.ParameterType == typeof(DateTime))
? new DateTimeParameterBinding(descriptor)
: null;
}
Моя привязка заключается в следующем:
// NullableDateTimeParameterBinding is the same just tries to return a nullable
// datetime instead of a standard datetime
public class DateTimeParameterBinding : HttpParameterBinding
{
private readonly HttpParameterBinding ModelBinding;
private readonly string[] ValidFormats = new string[]
{
"yyyy-MM-ddTHH:mm:ss.FK",
"yyyy-MM-ddTHH:mm:ss.FFK",
"yyyy-MM-ddTHH:mm:ss.FFFK",
"yyyy-MM-ddTHH:mm:ssK"
};
public DateTimeParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor)
{
ModelBinding = new ModelBinderAttribute().GetBinding(descriptor);
}
public override async Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext, CancellationToken cancellationToken)
{
await ModelBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken);
DateTime value = DateTime.MinValue;
// get the object value that was set on the queryParam coming into the API on the URL
KeyValuePair<string, string> keyPair = actionContext.Request.GetQueryNameValuePairs()
.Where(a => a.Key.Equals(Descriptor.ParameterName))
.FirstOrDefault();
// as long as a value was found, then attempt to process it
if (keyPair.Value != null)
{
DateTime dateTime = DateTime.MinValue;
bool isMatchFound = false;
string dateToParse = Convert.ToString(keyPair.Value);
if (!string.IsNullOrEmpty(dateToParse)) // as long as we have a value, try to date-ify it
{
foreach(string format in ValidFormats)
{
isMatchFound = DateTime.TryParseExact(dateToParse, format, CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal, out dateTime);
if (isMatchFound)
break;
}
}
if (!isMatchFound)
{
throw new ArgumentException("A valid UTC datetime was not provided. Unable to perform conversion. " +
"The provided date was: [" + dateToParse + "]. " +
"Valid formats include: [" + string.Join(", ", ValidFormats) + "].");
}
value = dateTime;
}
// Sets the dateTime for this parameter in the argument dictionary of the action context
SetValue(actionContext, value);
}
}
Мои маршруты контроллера API, которые работали до введения привязок, но теперь не работают:
[Route("api/things")]
public IHttpActionResult GetThings(DateTime startTime, DateTime endTime, int? groupIdentifier = null);
[Route("api/things")]
public IHttpActionResult GetThings(string classification = "", DateTime? startDate = null, DateTime? endDate = null, string block = null, DateTime? date = null)
Мой маршрут контроллера API, который работает до того, как привязки были введены, но теперь только связывает привязку, если DateTime
имеет обязательную силу, а не DateTime?
:
[Route("api/otherThings")]
public IHttpActionResult GetOtherThings(DateTime startTime, DateTime endTime)
URL называются:
/api/things/?date=2017-05-23T13%3A00%3A00Z&block=D
/api/otherThings?endTime=2016-12-02T00%3A00%3A00.000Z&startTime=2016-12-01T12%3A00%3A00.000Z
Сообщение об исключении:
Multiple actions were found that match the request: GetThings on type API.Controllers.ApiController GetThings on type API.Controllers.ApiController
Все, что было бы полезно, я хотел бы установить привязку, которая будет применяться к обоим DateTime
а также DateTime?
параметры запроса. Казалось, достаточно легко, пока не пришло время проверить это.