ApiExplorer does not recognize route attributes with custom type

喜欢而已 提交于 2019-12-04 05:05:30

问题


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?


回答1:


You need to:

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

After 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);
    }
}



回答2:


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?



来源:https://stackoverflow.com/questions/41179191/apiexplorer-does-not-recognize-route-attributes-with-custom-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!