防止 ASP.NET Web API 路由引擎扣除自定义方法名
Posted
技术标签:
【中文标题】防止 ASP.NET Web API 路由引擎扣除自定义方法名【英文标题】:Prevent ASP.NET Web API route engine from deducting custom method names 【发布时间】:2012-12-03 10:08:35 【问题描述】:我有两个方法的 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(..)
方法(由于@ 987654327@ 参数名称,我猜)并忽略路由的action
部分。
有什么办法可以避免这种行为并要求在 URL 中明确指定操作部分?
【问题讨论】:
【参考方案1】:几件事。首先是这条路线:
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
的形式提供一个来设置。
【讨论】:
不幸的是,您似乎弄错了,单段/api/v1/projects?userId
URL 被路由到ForUser()
操作。我猜这是因为ForUser()
动作有userId
参数,它与URL 中的userId
参数相匹配。 (我还编辑了问题并删除了误导性部分)
@AndrewKhmylov - 我想也许我解释得不够好。单段 URL 确实 确实匹配 ForUser 操作,但它这样做是因为它通过 api/v1/controller/id
而不是 api/v1/controller/action
。这是因为 URL 中没有指定操作。然后它会尽其所能,最有可能通过查看 ID 来尝试找到它认为的最佳匹配。如果您希望它进入特定匹配项,您需要更改路线以添加默认操作,或者将操作添加到您的 url,因为它目前不包含一个。【参考方案2】:
我不完全理解你的问题。
/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
【讨论】:
目的是使操作部分显式,以便 URL 有意义。现在,我可以将任何内容替换为操作名称,它仍将使用 ForUser 操作。至于添加“id”参数 - 我同意它可以工作,但这并不是我一直在寻找的(因为某些操作可能有多个参数)。还是谢谢。/api/v1/projects/ForUser/2
的解释与/api/v1/projects/ForUser?userId=2
相同。要添加更多参数,只需执行/api/v1/projects/ForUser?userId=2&userName=andrew
【参考方案3】:
我终于想出了满足我要求的解决方案。我将this answer 和 levib 和 user1797792 建议的想法结合到以下配置中:
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) );
请注意,这里的配置顺序很重要。
首先,带有 any 查询字符串的/api/v1/projects
请求(即使参数名称与其他操作的参数匹配)通过第二条路由分派到Get()
方法。这很重要,因为在实际项目中,我有一个自定义操作过滤器附加到此操作,它根据提供的请求参数过滤返回的IQueryable
。
api/v1/projects/forUser/1
-like 请求通过第一条路由分派到ForUser(int id)
方法。将 userId
参数重命名为 id
允许构造更清晰的 URL。
显然,这种方法有一些局限性,但在我的具体情况下,这就是我所需要的。
【讨论】:
以上是关于防止 ASP.NET Web API 路由引擎扣除自定义方法名的主要内容,如果未能解决你的问题,请参考以下文章
Asp.Net Web API 2第五课——Web API路由
防止 FormsAuthenticationModule 拦截 ASP.NET Web API 响应
Asp.Net Web API 2第六课——Web API路由和动作选择