阻止ASP.NET Web API路由引擎扣除自定义方法名称

时间:2012-12-03 10:08:36

标签: c# asp.net-mvc asp.net-web-api

我有两个方法的Web API控制器 - 假设第一个方法返回普通项目列表,第二个方法返回分配给特定用户的所有项目。

public class ProjectController: ApiController
{
    public IQueryable<Project> Get() { ... }

    [HttpGet]
    public IQueryable<Project> ForUser(int userId) { ... }
}

在这种情况下,方法实现并不重要。

还调整了Web API路由配置以支持自定义方法名称。

config.Routes.MapHttpRoute(
    "DefaultApi",
    "api/v1/{controller}/{id}",
    new { id = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
    "DefaultApiWithAction",
    "api/v1/{controller}/{action}");

它运行正常,我可以访问/api/v1/projects//api/v1/projects/forUser/个端点,但似乎路由引擎太聪明了,所以它决定/api/v1/projects?userId=1请求可能与{{1}匹配}}方法(由于ForUser(..)参数名称,我猜)并忽略路由的userId部分。

有没有办法避免这种行为,并要求在URL中明确指定操作部分?

3 个答案:

答案 0 :(得分:3)

夫妻俩。首先是这条路线:

config.Routes.MapHttpRoute(
    "DefaultApiWithAction",
    "api/v1/{controller}/{action}",
    new { id = RouteParameter.Optional });

没有“action”作为可选参数。您已将id作为可选项(我假设为拼写错误),但由于路径中不存在,因此您不会仅与一个辅助段匹配。只有包含两个部分(控制器和操作)的URL才会通过此路由。这个网址:

/api/v1/projects?userId=1

...包含单个细分,但不包含。此路由以及缺少第二个组件的任何其他路由将默认为此路由:

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

...只接受控制器和可选ID。您需要重新格式化给定的URL以获取操作参数,或重写路由以使操作可选,并根据需要设置默认值。这将取决于您的应用程序架构,但总是在简单性方面犯错误。路线可能变得非常复杂 - 通常更简单。

对于必需/可选路线组件,请记住以下两点:

  • 除非在匿名对象中将其设置为可选,否则所有路径段都是必需的。
  • 如果细分具有默认值,也可以排除细分,通过在placeholder = value形式的匿名对象中提供默认值来设置。

答案 1 :(得分:0)

我完全不明白你的问题。 不应该/api/v1/projects?userId=1确实调用ForUser操作吗?

无论如何,要做出必要的动作,请按照以下方式制作你的HttpRoute:

   name: "DefaultApi",
   routeTemplate: "api/v1/{controller}/{action}/{id}",
   defaults: new { id = System.Web.Http.RouteParameter.Optional });

现在你可以像这样打电话: /api/v1/projects/ForUser/2

答案 2 :(得分:0)

我终于找到了满足我要求的解决方案。我已将 levib user1797792 建议的this answer和想法合并到以下配置中:

config.Routes.MapHttpRoute(
    "DefaultApiWithActionAndOptionalId",
    "api/v1/{controller}/{action}/{id}",
    new {id = RouteParameter.Optional});

config.Routes.MapHttpRoute(
    "DefaultApiGet",
    "api/v1/{controller}",
    new { action = "Get" },
    new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });

请注意,配置顺序非常重要。

首先,带有任何查询字符串的/api/v1/projects请求(即使参数名称与其他操作的参数匹配)也会通过第二个调度发送到Get()方法路线。这很重要,因为在实际项目中,我有一个附加到此操作的自定义操作过滤器,它根据提供的请求参数过滤返回的IQueryable

api/v1/projects/forUser/1 - 类似请求将通过第一条路由发送到ForUser(int id)方法。将userId参数重命名为id,可以构建更清晰的网址。

显然,这种方法有一些局限性,但在我的具体情况下,这就是我所需要的。