ASP.NET Web API 中的自定义方法名称
Posted
技术标签:
【中文标题】ASP.NET Web API 中的自定义方法名称【英文标题】:Custom method names in ASP.NET Web API 【发布时间】:2012-03-23 01:54:45 【问题描述】:我正在从 WCF Web API 转换为新的 ASP.NET MVC 4 Web API。我有一个 UsersController,我想要一个名为 Authenticate 的方法。我看到了如何执行 GetAll、GetOne、Post 和 Delete 的示例,但是如果我想在这些服务中添加额外的方法怎么办?例如,我的 UsersService 应该有一个名为 Authenticate 的方法,他们在其中传递用户名和密码,但它不起作用。
public class UsersController : BaseApiController
public string GetAll()
return "getall!";
public string Get(int id)
return "get 1! " + id;
public User GetAuthenticate(string userName, string password, string applicationName)
LogWriter.Write(String.Format("Received authenticate request for username 0 and password 1 and application 2",
userName, password, applicationName));
//check if valid leapfrog login.
var decodedUsername = userName.Replace("%40", "@");
var encodedPassword = password.Length > 0 ? Utility.HashString(password) : String.Empty;
var leapFrogUsers = LeapFrogUserData.FindAll(decodedUsername, encodedPassword);
if (leapFrogUsers.Count > 0)
return new User
Id = (uint)leapFrogUsers[0].Id,
Guid = leapFrogUsers[0].Guid
;
else
throw new HttpResponseException("Invalid login credentials");
我可以浏览到 myapi/api/users/,它会调用 GetAll,我可以浏览到 myapi/api/users/1,它会调用 Get,但是如果我调用 myapi/api/users/authenticate?username= 0&password=1 然后它会调用 Get (NOT Authenticate) 和错误:
参数字典包含“Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController”中方法“System.String Get(Int32)”的不可空类型“System.Int32”的参数“id”的空条目.可选参数必须是引用类型、可空类型或声明为可选参数。
如何调用 Authenticate 等自定义方法名称?
【问题讨论】:
请参考这个链接:第5个回答***.com/questions/12775590/… 【参考方案1】:默认情况下,路由配置遵循 RESTFul 约定,这意味着它将仅接受 Get、Post、Put 和 Delete 操作名称(查看 global.asax 中的路由 => 默认情况下它不允许您指定任何操作name => 它使用 HTTP 动词来调度)。因此,当您向 /api/users/authenticate
发送 GET 请求时,您基本上是在调用 Get(int id)
操作并传递 id=authenticate
,这显然会崩溃,因为您的 Get 操作需要一个整数。
如果你想拥有不同于标准的动作名称,你可以在global.asax
修改你的路由定义:
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/action/id",
defaults: new action = "get", id = RouteParameter.Optional
);
现在您可以导航到/api/users/getauthenticate
来验证用户身份。
【讨论】:
有没有办法让它在仍然允许其他操作的同时仍然使用 Get(id)、Get() Put、Delete、Post? @ShawnMclean 我猜你可以指定另一个没有action
的路线,它对id
有限制,这样int
或Guid
(或其他)以外的任何东西都不会匹配.那么它应该能够落入达林建议的那个
这里更重要的一点是,使用这种路由风格,您必须使用属性来指定允许的 HTTP 方法(如 [HttpGet])。
您确定需要使用其他操作吗?您是否真的尝试在 REST 约定中适应您正在做的事情?应该不需要使用其他操作。
@niico:假设你想要一个 Count() 方法,它返回 Get() 将返回的元素数。我不知道如何将其放入 Get()、Get(id)、Post(...)、Put(...) 或 Delete(id) 中。而且,当然,我可以想象到更多可能的方法。【参考方案2】:
这是迄今为止我想出的最好的方法,它可以合并额外的 GET 方法,同时也支持普通的 REST 方法。将以下路由添加到您的 WebApiConfig:
routes.MapHttpRoute("DefaultApiWithId", "Api/controller/id", new id = RouteParameter.Optional , new id = @"\d+" );
routes.MapHttpRoute("DefaultApiWithAction", "Api/controller/action");
routes.MapHttpRoute("DefaultApiGet", "Api/controller", new action = "Get" , new httpMethod = new HttpMethodConstraint(HttpMethod.Get) );
routes.MapHttpRoute("DefaultApiPost", "Api/controller", new action = "Post", new httpMethod = new HttpMethodConstraint(HttpMethod.Post));
我用下面的测试类验证了这个解决方案。我能够成功地在下面的控制器中使用每种方法:
public class TestController : ApiController
public string Get()
return string.Empty;
public string Get(int id)
return string.Empty;
public string GetAll()
return string.Empty;
public void Post([FromBody]string value)
public void Put(int id, [FromBody]string value)
public void Delete(int id)
我验证它支持以下请求:
GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1
注意如果您的额外 GET 操作不以“Get”开头,您可能需要在方法中添加一个 HttpGet 属性。
【讨论】:
不错的解决方案,你能告诉我,如果我配置put
和 delete
动词,就像你在 get
和 post
上所做的那样,也可以正常工作吗?
在我看来,这应该包含在 WebAPI 项目的默认值中(可能被注释掉)。它同时为您提供 WebAPI 和 MVC 风格的路由......
@FelipeOriani,我认为您不需要或不需要配置 put
或 delete
动词,因为这些请求通常会伴随一个 id 参数来标识您希望应用该操作的资源到。对/api/foo
的delete
调用应该会引发错误,因为您要删除哪个foo?因此 DefaultApiWithId 路由应该可以很好地处理这些情况。
这对我来说根本不起作用。当我尝试执行基本 GET 时收到错误消息。
对于第一个,DefaultApiWithId,默认值不应该是 null 而不是 new id = RouteParameter.Optional ? 'id' 不是必需的吗?【参考方案3】:
我已经进入了 MVC4 世界。
不管怎样,我有一个 SitesAPIController,我需要一个自定义方法,可以这样调用:
http://localhost:9000/api/SitesAPI/Disposition/0
最后一个参数使用不同的值来获得不同处置的记录。
最后对我有用的是:
SitesAPIController 中的方法:
// GET api/SitesAPI/Disposition/1
[ActionName("Disposition")]
[HttpGet]
public Site Disposition(int disposition)
Site site = db.Sites.Where(s => s.Disposition == disposition).First();
return site;
这在 WebApiConfig.cs 中
// this was already there
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional
);
// this i added
config.Routes.MapHttpRoute(
name: "Action",
routeTemplate: "api/controller/action/disposition"
);
只要我将 disposition 命名为 id,我就会遇到:
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:9000/api/SitesAPI/Disposition/0'.",
"MessageDetail": "No action was found on the controller 'SitesAPI' that matches the request."
当我将它重命名为 disposition 时,它开始工作。显然参数名称与占位符中的值匹配。
请随意编辑此答案以使其更准确/解释性更强。
【讨论】:
感谢您的提示。我和你犯了同样的错误。【参考方案4】:Web Api 默认需要 api/controller/id 形式的 URL,以覆盖此默认路由。您可以通过以下两种方式设置路由。
第一个选项:
在WebApiConfig.cs下面添加路由注册
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/controller/action/id",
defaults: new id = RouteParameter.Optional
);
使用 HttpGet 和以下参数装饰您的操作方法
[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
string param2, string param3)
// your code here
调用上述方法的 url 如下所示
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1¶m2=value2¶m3=value3
第二个选项 将路由前缀添加到 Controller 类并使用 HttpGet 装饰您的操作方法,如下所示。 在这种情况下,无需更改任何 WebApiConfig.cs。它可以有默认路由。
[RoutePrefix("api/controller/action")]
public class MyDataController : ApiController
[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
string param2, string param3)
// your code here
调用上述方法的 url 如下所示
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1¶m2=value2¶m3=value3
【讨论】:
我非常喜欢第二个选项。你能告诉我如何在 VB.net 中使用它吗?非常感谢。【参考方案5】:如果您将 ASP.NET 5 与 ASP.NET MVC 6 一起使用,这些答案中的大多数根本不起作用,因为您通常会让 MVC为您创建适当的路由集合(使用默认的 RESTful 约定),这意味着您不会找到任何 Routes.MapRoute()
调用来随意编辑。
Startup.cs
文件调用的ConfigureServices()
方法将向 ASP.NET 5 中内置的依赖注入框架注册 MVC:这样,当您稍后在该类中调用 ApplicationBuilder.UseMvc()
时,MVC 框架将自动添加这些默认路由到您的应用程序。我们可以通过查看框架源代码中的UseMvc()
方法实现来了解幕后发生的事情:
public static IApplicationBuilder UseMvc(
[NotNull] this IApplicationBuilder app,
[NotNull] Action<IRouteBuilder> configureRoutes)
// Verify if AddMvc was done before calling UseMvc
// We use the MvcMarkerService to make sure if all the services were added.
MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);
var routes = new RouteBuilder
DefaultHandler = new MvcRouteHandler(),
ServiceProvider = app.ApplicationServices
;
configureRoutes(routes);
// Adding the attribute route comes after running the user-code because
// we want to respect any changes to the DefaultHandler.
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
routes.DefaultHandler,
app.ApplicationServices));
return app.UseRouter(routes.Build());
这样做的好处是框架现在可以处理所有繁重的工作,遍历所有控制器的操作并设置它们的默认路由,从而为您节省一些多余的工作。
不好的是,关于如何添加自己的路线的文档很少或根本没有。幸运的是,您可以使用基于约定的和/或基于属性的方法(又名属性路由)轻松做到这一点。
基于约定的
在您的 Startup.cs 类中,替换为:
app.UseMvc();
用这个:
app.UseMvc(routes =>
// Route Sample A
routes.MapRoute(
name: "RouteSampleA",
template: "MyOwnGet",
defaults: new controller = "Items", action = "Get"
);
// Route Sample B
routes.MapRoute(
name: "RouteSampleB",
template: "MyOwnPost",
defaults: new controller = "Items", action = "Post"
);
);
基于属性的
MVC6 的一大优点是,您还可以通过使用适当的 RouteAttribute
和/或 HttpGet
装饰 Controller
类和/或 Action
方法来基于每个控制器定义路由HttpPost
模板参数,如:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
namespace MyNamespace.Controllers
[Route("api/[controller]")]
public class ItemsController : Controller
// GET: api/items
[HttpGet()]
public IEnumerable<string> Get()
return GetLatestItems();
// GET: api/items/5
[HttpGet("num")]
public IEnumerable<string> Get(int num)
return GetLatestItems(5);
// GET: api/items/GetLatestItems
[HttpGet("GetLatestItems")]
public IEnumerable<string> GetLatestItems()
return GetLatestItems(5);
// GET api/items/GetLatestItems/5
[HttpGet("GetLatestItems/num")]
public IEnumerable<string> GetLatestItems(int num)
return new string[] "test", "test2" ;
// POST: /api/items/PostSomething
[HttpPost("PostSomething")]
public IActionResult Post([FromBody]string someData)
return Content("OK, got it!");
此控制器将处理以下请求:
[GET] api/items
[GET] api/items/5
[GET] api/items/GetLatestItems
[GET] api/items/GetLatestItems/5
[POST] api/items/PostSomething
另外请注意,如果您同时使用这两种方法,基于属性的路由(如果已定义)将覆盖基于约定的路由,并且它们都将覆盖UseMvc()
定义的默认路由。
更多信息,您也可以在我的博客上read the following post。
【讨论】:
这太完美了!其他答案都没有真正做到我需要的。但是你救了我:) 有没有办法使用预定义的模型作为第二个参数?例如,当我像这样修补某个用户时:public IActionResult Patch(int id, [FromQuery] Person person)
,所有传入的属性都是空的!【参考方案6】:
有关命名操作的详细讨论,请参阅本文。它还表明您可以使用 [HttpGet] 属性而不是在操作名称前加上“get”。
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
【讨论】:
【参考方案7】:Web APi 2 及更高版本支持一种新的路由类型,称为属性路由。顾名思义,属性路由使用属性来定义路由。属性路由让您可以更好地控制 Web API 中的 URI。例如,您可以轻松创建描述资源层次结构的 URI。
例如:
[Route("customers/customerId/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) ...
将完美,您不需要任何额外的代码,例如在 WebApiConfig.cs 中。 只是你必须确保 WebApiConfig.cs 中启用了 web api 路由,如果没有,你可以像下面这样激活:
// Web API routes
config.MapHttpAttributeRoutes();
您无需在 WebApiConfig.cs 中执行更多操作或更改某些内容。更多详情可以关注this article。
【讨论】:
【参考方案8】:只需如下修改您的 WebAPIConfig.cs
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/action/id",
defaults: new action = "get", id = RouteParameter.Optional );
然后按照下面的方式实现您的 API
// GET: api/Controller_Name/Show/1
[ActionName("Show")]
[HttpGet]
public EventPlanner Id(int id)
【讨论】:
以上是关于ASP.NET Web API 中的自定义方法名称的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 api 连接器和 asp net core web api 通过自定义声明丰富 azure b2c 令牌