【问题标题】:Possible to have a WebAPI method exposed (Get or Post) that can be called like an Action?是否可以公开一个可以像操作一样调用的 WebAPI 方法(Get 或 Post)?
【发布时间】:2016-03-24 18:47:17
【问题描述】:

这可能不是很清楚,所以让我详细说明一下:

据我所知,web api 控制器的“动作”是由参数的数量和类型决定的。因此,如果您传入两个字符串,您将获得在方法签名中包含两个字符串的 Get 或 Post。

我想做的是设置几条这样的路线:

config.Routes.MapHttpRoute(
    name: "fileFunctionsDelete",
    routeTemplate: "api/{controller}/{appName}/{userID}/Delete",
    defaults: new {
        appName = RouteParameter.Optional,
        userID= RouteParameter.Optional,
        fileAction= RouteParameter.Optional
    }
);

config.Routes.MapHttpRoute(
    name: "fileFunctionsUpload",
    routeTemplate: "api/{controller}/{appName}/{userID}/Upload",
    defaults: new {
        appName = RouteParameter.Optional,
        userID= RouteParameter.Optional,
        fileAction= RouteParameter.Optional
    }
);

这些显然应该转到同一个控制器,但在 MVC 网站中,我会转到名为“删除”或“上传”的操作。

到目前为止,我看到的只是 Gets 和 Posts,其中包含用于确定调用的参数的数量和类型。这太令人困惑了。

有什么方法可以让它在调用行为上更像“MVC 网站”?


澄清一下:如果我想触发一个特定的动作,我会直呼其名。 “public ActionResult ThisAction(object[] params)。在 web api 中,所有的签名都像 Get 或 Put 或 Post。我希望能够说像“url:../Delete”这样的话,它会调用适当的删除获取或发布。我不想通过传入的参数数量来区分我的方法或解析字符串以查看用户的意图。这些都是失败的秘诀

更新:

Kenneth、lgor 和 Tu Hoang 的答案都包含需要综合考虑的关键信息(限制路线、动作、模型属性等的方法),并且每个人都包含可以得出完整答案的信息。我无法全部标记,所以我将我的问题标记为“最正确”。

【问题讨论】:

  • 我不明白你的问题,MVC 控制器和 WebAPI 控制器在很大程度上是同一件事,实际上在 ASP.NET MVC Core 中它们结合了所有内容。是什么让您感到困惑?
  • 如果我想触发一个特定的动作,我会直呼其名。 “public ActionResult ThisAction(object[] params)。在 web api 中,所有的签名都像 Get 或 Put 或 Post。我希望能够说像“url:../Delete”这样的话,它会调用适当的删除获取或发布。我不想通过传入的参数数量来区分我的方法或解析字符串以查看用户的意图。这些都是失败的秘诀。
  • 对不起,如果我很密集,您是否想了解如何在控制器上使用 HTTP 动词以及如何使用 Html.ActionLink 和这些动词?
  • 你看过attribute routing了吗?它比修改路线配置更专注于应用,IMO。
  • 你注意到链接文章的标题了吗?

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


【解决方案1】:

就我个人而言,当您希望在操作上使用自定义路由时,我发现属性路由更容易使用。 As exemplified 在 ASP.NET 官方网站上,您可以使用 [Route] 属性将路由直接应用于您的操作:

例如

public class CustomerController : ApiController
{
    [Route("api/Customer/{model.LastName}/{model.FirstName}/Upload")]
    public void Post(CustomerModel model)
    {
         // ...
    }
}

我不记得你是否可以像在默认路由中那样替换{controller},但我相信你可以。我有一个复杂的对象作为参数传入,因此使用点表示法,但如果你只是使用简单的类型,那么你可以直接使用变量名(例如[Route("api/Customer/{lastName}/{firstName}/Upload")])。

您还可以将[RoutePrefix] 属性应用于控制器,这样您就不必在每个操作上重复api/Customer/

[RoutePrefix("api/Customer")]
public class CustomerController : ApiController
{
    [Route("{model.LastName}/{model.FirstName}/Upload")]
    public void Post(CustomerModel model)

这不是一个非常好的示例,因为我正在演示一个 POST 并且参数是通过 URL 传递的,但它应该演示这个想法。

【讨论】:

  • 这与您提供的链接相结合绝对令人难以置信。我将实施的其他答案中还有其他一些内容,但是,哇。谢谢。
【解决方案2】:

像下面这样?您需要 action 默认值并在控制器操作上匹配 ActionName 属性。

话虽如此,您的路线示例看起来需要一些工作。具体来说,让所有路由参数可选是什么意思。如果userId 是可选的,删除会做什么?如果appName 是可选的,路由甚至会有效。

config.Routes.MapHttpRoute(
    name: "fileFunctionsDelete",
    routeTemplate: "api/{controller}/{appName}/{userID}/Delete",
    defaults: new {
        appName = RouteParameter.Optional,
        userID= RouteParameter.Optional,
        action = "Delete"
    },
    constraints: new { HttpMethod = new HttpMethodConstraint(HttpMethod.Post) });
);

[HttpPost]
[ActionName("Delete")]
public async Task<HttpResponseMessage> DeleteUser(
    string controller,
    string appName,
    string userID)
{
}

【讨论】:

  • 这可能是我一直在寻找的最干净的解决方案。一件事:{Controller} 未在路由的参数列表中指定,因此与任何具有“正确名称”的控制器匹配。我会试试这个,如果它不起作用,我会告诉你。
  • 最终,我不喜欢这个,因为它不能与我需要实现的 30 种左右的方法一起扩展(不是我的选择),但你的路线中的限制是纯粹的魔法。那肯定会进去。谢谢。
【解决方案3】:

您可以像在 MVC 中一样在 routeTemplate 中指定 action 占位符。看下面的代码,我把Delete换成了{action}。如果你在参数之间是可选的,那么它可能会变得棘手,所以这里的顺序可能很关键,我更喜欢它是api/{controller}/{action}/{appName}/{userID}/api/{action}/{controller}/{appName}/{userID}/

config.Routes.MapHttpRoute(
    name: "fileFunctionsDelete",
    routeTemplate: "api/{controller}/{appName}/{userID}/{action}",
    defaults: new {
        appName = RouteParameter.Optional,
        userID= RouteParameter.Optional,
        fileAction= RouteParameter.Optional
    }
);

(来自评论)...带有示例方法签名

方法签名与任何其他签名一样。

// I am not sure what type of parameters these are so I am going to take a wild guess
[HttpPost]
public async Task<IHttpActionResult> Post(string appName = null, int? userID = null, CancellationToken token) {}

这仅取决于您传递的内容。对于您可能拥有的 post、put、get、delete 或自定义名称,这同样适用。如果您想根据 http 动词限制访问,请记住应用 HttpGet/HttpPost/HttpPut/HttpDelete 属性(这里没有什么不同)。


您还可以使用 Kennith 指定的 Route 和 RoutePrefix 属性。我不会偷他的答案,这是一个很好的答案,如果根据情况不喜欢的话,也是完全可以接受的。

【讨论】:

  • 感谢有关参数订购的提示。我要实现它。很好的建议,谢谢。
猜你喜欢
  • 2014-04-04
  • 2013-09-21
  • 2014-10-20
  • 2016-06-02
  • 2012-07-21
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 1970-01-01
相关资源
最近更新 更多