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

C# 防止ASP.NET Web API路由引擎扣除自定义方法名称,c#,asp.net-mvc,asp.net-web-api,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控制器有两个方法——假设第一个方法返回普通项目列表,第二个方法返回分配给特定用户的所有项目

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

    [HttpGet]
    public IQueryable<Project> ForUser(int userId) { ... }
}
它工作得很好,我可以访问
/api/v1/projects/
/api/v1/projects/forUser/
端点,但似乎路由引擎太聪明了,所以它决定
/api/v1/projects?userId=1
请求可能与
forUser(…)
方法匹配(我猜是因为
userId
参数名)并忽略路由的
{action}
部分


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

我不完全理解您的问题。
/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

两件事结合起来。首先,这条路线:

config.Routes.MapHttpRoute(
    "DefaultApiWithAction",
    "api/v1/{controller}/{action}",
    new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/v1/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
没有“action”作为可选参数。您已将
id
作为可选项(我假设为输入错误),但由于它不存在于路线中,您将无法获得仅一个补充段的匹配。只有包含控制器和操作两部分的URL才会通过此路由。此url:

/api/v1/projects?userId=1
…包含一个单独的段,并且不会。此路由以及缺少第二个组件的任何其他路由将默认为此路由:

config.Routes.MapHttpRoute(
    "DefaultApiWithAction",
    "api/v1/{controller}/{action}",
    new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/v1/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
…只接受控制器和可选ID。您需要重新格式化给定URL以获取操作参数,或者重写路由以使操作可选,并根据需要设置默认值。这一切都取决于您的应用程序体系结构,但总是在简单性方面出错。路线可能变得非常复杂——通常越简单越好

对于所需/可选的管线组件,请记住以下两点:

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

我终于找到了满足我需求的解决方案。我已将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) });
请注意,配置顺序在这里非常重要

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

api/v1/projects/forUser/1
-like请求通过第一个路由发送到
forUser(int-id)
方法。将
userId
参数重命名为
id
可以构造更干净的URL


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

目的是使动作部分显式,这样URL才有意义。现在,我可以将任何内容替换为操作名称,它仍将用于用户操作。至于添加“id”参数,我同意它是有效的,但这并不完全是我一直在寻找的(因为某些操作可能有多个参数)。谢谢。
/api/v1/projects/ForUser/2
/api/v1/projects/ForUser?userId=2
相同。要添加更多参数,只需执行
/api/v1/projects/ForUser?userId=2&userName=andrew
不幸的是,似乎您弄错了,一段
/api/v1/projects?userId
URL被路由到
ForUser()
操作。我猜这是因为
ForUser()
操作具有
userId
参数,该参数与URL中的
userId
参数相匹配。(我还编辑了这个问题并删除了误导性的部分)@AndrewKhmylov-我想可能是我解释得不够好。单段URL确实与ForUser操作匹配,但它这样做是因为它通过
api/v1/{controller}/{id}
而不是
api/v1/{controller}/{action}
。这是因为URL中没有指定任何操作。然后,它会做它想做的事情,很可能是通过查看ID,试图找到它认为最好的匹配项。如果希望它转到特定的匹配项,则需要更改路由以添加默认操作,或者将该操作添加到url,因为它当前不包含默认操作。