ASP.NET Web Api在选择操作时忽略RouteParameter.Optional。

5
我通过以下步骤找到了这个问题:
  1. 使用已安装的项目模板创建一个新的WebApi项目

  2. 转到Controllers / ValuesController.cs,有两个像这样的Get方法:

    public IEnumerable<string> Get() //此方法提供GetAll函数

    public string Get(int id) //此方法是GetOneById

  3. 我不喜欢这种设计,因为我认为这两个API方法可以合并为一个:

    public IEnumerable<string> Get(string ids) 当ids为空时,它返回所有记录,否则根据ids返回结果(看起来像id1,id2,id3 ...)

我还修改了路由:

    config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{ids}",
            defaults: new { ids = RouteParameter.Optional }
        );

现在我认为一切都准备好了,但是当我在浏览器中访问/values(没有指定ids参数)时,会提示“未找到任何操作”,直到我为ids添加了默认值:

public IEnumerable<string> Get(string ids = null)

从现在开始一切都按我预期的方式运作。 但是我无法移动路由中的“defaults”参数。 这意味着:代码将ids参数声明为可选两次。
查看位于System.Web.Http.Controllers.ActionSelectorCacheItem.FindActionUsingRouteAndQueryParameters方法的MVC4源代码,我可以看到在查找正确的操作时忽略了在路由中定义的可选参数。 我个人认为这很糟糕,即使解决方法也很简单。 或者我误解了路由和操作方法之间的关系? 我认为路由是用来帮助请求找到其相应操作的规则,而“参数是可选的”正是规则的一部分。
2个回答

4
您可能会认为这个解决方案对于相对简单的问题来说有些笨拙,但我认为根本原因在于您试图将“GetAll”和“GetOneById”合并到同一个方法中。
如果您愿意将控制器中的方法拆分出来,则可以避免路由配置的问题。在我看来,这并不是什么坏事;它符合关注点分离原则。同时,这也不意味着必须重复编写代码,因为您可以将访问要返回的对象的逻辑集中到一起。
这些控制器方法应该可以为您提供所需的路由,而无需调整路由配置:
[HttpGet]
public IEnumerable<string> GetAll()
{
    return GetStrings(new int[]{});
}

[HttpGet]
public string GetOneById(int id)
{
    return GetStrings(new int[]{id}).FirstOrDefault();
}

private IEnumerable<string> GetStrings(int[] ids)
{
     return // fetch strings using int array
}

1

您可以选择将值作为查询字符串

public class PaymentsController : ApiController {
        [HttpGet]
        public Person GetValues(string ids = null)
        {
            return new Person() { FirstName = "Michael" };
        }
    }

    public class Person
    {
        public string FirstName { get; set; }
    }
}

我能够通过以下两种方式访问此路由:

  1. /api/payments/getvalues/?ids=123
  2. /api/payments/getvalues/

注意:除了默认路由之外,没有任何特殊路由。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接