ApiExplorer does not recognize custom attributes of a route

I have a project where I want to use custom attributes of a route. The following code, in which I have a custom type as a query parameter, works fine, and the help page displays a custom type.

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

WebApi.HelpPage provides the following documentation Help: Page

If I change the code to use route attributes, the result is that I get a blank help page.

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

When I check the code that I observe in HelpController.cs, ApiExplorer.ApiDescriptions returns an empty ApiDescriptions collection

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

Is there a way to make ApiExplorer recognize my own IntegerListParameter class as attribute routing?

+1
source share
2 answers

You need:

  • add HttpParameterBinding for your type IntegerListParameter
  • mark the binding as IValueProviderParameterBinding and implement ValueProviderFactories
  • add a converter for IntegerListParameter and override the CanConvertFrom method for the typeof(string) parameter

After these steps, the route with the user-defined type IntegerListParameter must be recognized in ApiExplorer.

See my example for the ObjectId type:

 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); } } 
+1
source

It's unclear what the structure of the IntegerListParameter data structure is, but if you need to send a list of comma-separated integers to the request (e.g. ~api/products?ids=1,2,3,4 ), you can use filter attributes. An example implementation of this can be found here: Use a custom action filter to use the web API?

0
source

Source: https://habr.com/ru/post/1492636/


All Articles