Настраиваемый 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? параметры запроса. Казалось, достаточно легко, пока не пришло время проверить это.

0 ответов

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