Reputation: 21
I have a project where I want to use route attributes with a custom type. The following code where I have the custom type as a query parameter works fine and the help page displays the custom type.
// GET api/values?5,6
[Route("api/values")]
public string Get(IntegerListParameter ids)
{
return "value";
}
WebApi.HelpPage gives the following documentation Help:Page
If I change the code to use route attributes, the result is that I get an empty help page.
// GET api/values/5,6
[Route("api/values/{ids}")]
public string Get(IntegerListParameter ids)
{
return "value";
}
When I inspect the code I observe in HelpController.cs that ApiExplorer.ApiDescriptions returns an empty collection of ApiDescriptions
public ActionResult Index()
{
ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();
Collection<ApiDescription> apiDescriptions = Configuration.Services.GetApiExplorer().ApiDescriptions;
return View(apiDescriptions);
}
Is there any way to get ApiExplorer to recognize my custom class IntegerListParameter as attribute routing?
Upvotes: 2
Views: 2605
Reputation: 4030
You need to:
IntegerListParameter
typeIValueProviderParameterBinding
and implement ValueProviderFactories
IntegerListParameter
and override CanConvertFrom
method for typeof(string)
parameterAfter these actions, route with custom type IntegerListParameter must be recognized in ApiExplorer.
See my example for type 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);
}
}
Upvotes: 2
Reputation: 7285
Not exactly sure what data structure IntegerListParameter
list is but if you need to send a comma delimited list of integers in the query(e.g. ~api/products?ids=1,2,3,4
) you can use filter attributes. An example implementation of this can be found here: Convert custom action filter for Web API use?
Upvotes: 0