我有一个简单的控制器,带有无参数Get和Get,它接受一个id。
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 for a single resource接受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和获取我的自定义标识?
答案 0 :(得分: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());
不幸的是,这意味着参数列表中只包含简单类型(primitive,datetime,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());
我希望我错了。如果事实证明我被困住了,请回来并在冷却期后将其标记为答案。