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 ответа
Вам нужно:
- добавить HttpParameterBinding для вашего
IntegerListParameter
тип - пометить привязку как
IValueProviderParameterBinding
и реализоватьValueProviderFactories
- добавить конвертер для
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?