MVC4 Web API中的struct导致路由中断

时间:2013-03-15 06:02:26

标签: asp.net-mvc-4 asp.net-web-api-routing

我有一个简单的控制器,带有无参数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和获取我的自定义标识?

1 个答案:

答案 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());

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