ApiExplorer не распознает атрибуты маршрута с пользовательским типом

У меня есть проект, где я хочу использовать атрибуты маршрута с пользовательским типом. Следующий код, где у меня есть пользовательский тип в качестве параметра запроса, работает нормально, и на странице справки отображается пользовательский тип.

// GET api/values?5,6
[Route("api/values")]
public string Get(IntegerListParameter ids)
{
    return "value";
}

WebApi.HelpPage предоставляет следующую справочную документацию : Страница

Если я изменю код для использования атрибутов маршрута, в результате я получу пустую страницу справки.

// GET api/values/5,6
[Route("api/values/{ids}")]
public string Get(IntegerListParameter ids)
{
    return "value";
}

Когда я проверяю код, который я наблюдаю в HelpController.cs, ApiExplorer.ApiDescription возвращает пустую коллекцию ApiDescription.

public ActionResult Index()
{
    ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();
    Collection<ApiDescription> apiDescriptions = Configuration.Services.GetApiExplorer().ApiDescriptions;

    return View(apiDescriptions);
}

Есть ли способ заставить ApiExplorer распознавать мой пользовательский класс IntegerListParameter в качестве атрибута маршрутизации?

2 ответа

Вам нужно:

  1. добавить HttpParameterBinding для вашего IntegerListParameter тип
  2. пометить привязку как IValueProviderParameterBinding и реализовать ValueProviderFactories
  3. добавить конвертер для IntegerListParameter и переопределить CanConvertFrom метод для typeof(string) параметр

После этих действий маршрут с пользовательским типом IntegerListParameter должен быть распознан в ApiExplorer.

Смотрите мой пример для типа ObjectId:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //...
        config.ParameterBindingRules.Insert(0, GetCustomParameterBinding);
        TypeDescriptor.AddAttributes(typeof(ObjectId), new TypeConverterAttribute(typeof(ObjectIdConverter)));
        //...
    }

    public static HttpParameterBinding GetCustomParameterBinding(HttpParameterDescriptor descriptor)
    {
        if (descriptor.ParameterType == typeof(ObjectId))
        {
            return new ObjectIdParameterBinding(descriptor);
        }
        // any other types, let the default parameter binding handle
        return null;
    }
}

public class ObjectIdParameterBinding : HttpParameterBinding, IValueProviderParameterBinding
{
    public ObjectIdParameterBinding(HttpParameterDescriptor desc)
        : base(desc)
    {
    }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        try
        {
            SetValue(actionContext, new ObjectId(actionContext.ControllerContext.RouteData.Values[Descriptor.ParameterName] as string));
            return Task.CompletedTask;
        }
        catch (FormatException)
        {
            throw new BadRequestException("Invalid id format");
        }
    }

    public IEnumerable<ValueProviderFactory> ValueProviderFactories { get; } = new[] { new QueryStringValueProviderFactory() };
}

public class ObjectIdConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;
        return base.CanConvertFrom(context, sourceType);
    }
}

Не совсем уверен, что структура данных IntegerListParameter список, но если вам нужно отправить в запросе список целых чисел через запятую (например, ~api/products?ids=1,2,3,4) вы можете использовать атрибуты фильтра. Пример реализации этого можно найти здесь: Преобразовать фильтр пользовательских действий для использования веб-API?

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