2

我有一个简单的控制器,它带有一个无参数的 Get 和一个带有 id 的 Get。

public class BooksController : ApiController
{
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    public string Get(Identity id)
    {
        return "value";
    }
}

Identity 是一个自定义结构,简化后如下所示:

public struct Identity
{
    // ....
    public Identity(Guid value)
    {
        internalValue = value;
    }
}

但是,如果我尝试导航到任一端点,我会收到一条错误消息,指出找到了与请求匹配的多个操作。如果单个资源的 Get 采用 Guid 而不是我的 Identity 结构,则 BooksController 工作得很好。

我的自定义模型活页夹和接线:

public class IdentityModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        Identity identity = new Identity(Guid.Parse(bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue));
        bindingContext.Model = identity;
        return true;
    }
}

GlobalConfiguration.Configuration.BindParameter(typeof(Identity), new IdentityModelBinder());

请注意,如果将任何参数添加到无参数 Get 中,则上述绑定很好。也就是说,如果我将 BooksController 更改为:

public class BooksController : ApiController
{
    public IEnumerable<string> Get(int page, string searchTerm)
    {
        return new string[] { "value1", "value2" };
    }

    public string Get(Identity id)
    {
        return "value";
    }
}

我的路由配置只是开箱即用的示例:

    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
        );
    }

然后我可以正确导航到两个端点。如何设置我的 BooksController 以允许无参数 Get 和接受我的自定义身份的 Get?

4

1 回答 1

0

我想我已经找到了麻烦的根源,但我没有找到解决办法。

在 Web API ApiControllerActionSelector 中,特别是在 ActionCacheSelectorItem.ctor 中,解析动作参数的逻辑是:

_actionParameterNames.Add(
       actionDescriptor,
       actionBinding.ParameterBindings
                    .Where(binding => !binding.Descriptor.IsOptional && TypeHelper.IsSimpleUnderlyingType(binding.Descriptor.ParameterType) && binding.WillReadUri())
                    .Select(binding => binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName).ToArray());

不幸的是,这意味着参数列表中只会包含简单类型(原始类型、日期时间、guid 等)。如果我扩展上面的过滤器逻辑以考虑参数是否可以像这样从字符串转换,我可以让我的结构显示在参数列表中:

_actionParameterNames.Add(
       actionDescriptor,
       actionBinding.ParameterBindings
                    .Where(binding => !binding.Descriptor.IsOptional && 
                          (TypeHelper.IsSimpleUnderlyingType(binding.Descriptor.ParameterType) ||
                          TypeHelper.HasStringConverter(binding.Descriptor.ParameterType)) && 
                          binding.WillReadUri())
                    .Select(binding => binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName).ToArray());

我希望我错了。如果事实证明我被卡住了,我会在冷却期后回来并将其标记为答案。

于 2013-03-15T18:15:30.953 回答