2

我有带有 2 种方法的 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请求可能与ForUser(..)方法匹配(我猜是由于userId参数名称)并忽略{action}了路由的一部分。

有什么方法可以避免这种行为并要求在 URL 中明确指定操作部分?

4

3 回答 3

3

夫妇的事情。首先这条路线:

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

没有“动作”作为可选参数。您已将其包含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.
于 2012-12-03T10:26:00.247 回答
0

我终于想出了满足我要求的解决方案。我已将levibuser1797792建议的这个答案和想法结合到以下配置中:

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-like 请求ForUser(int id)通过第一条路由分派给方法。userId将参数重命名为id允许构造更清晰的 URL。

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

于 2012-12-03T14:14:34.257 回答
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

于 2012-12-03T10:25:50.683 回答