C# ASP.NET Web API 中的自定义方法名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9569270/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom method names in ASP.NET Web API
提问by Justin
I'm converting from the WCF Web API to the new ASP.NET MVC 4 Web API. I have a UsersController, and I want to have a method named Authenticate. I see examples of how to do GetAll, GetOne, Post, and Delete, however what if I want to add extra methods into these services? For instance, my UsersService should have a method called Authenticate where they pass in a username and password, however it doesn't work.
我正在从 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");
}
}
I can browse to myapi/api/users/ and it will call GetAll and I can browse to myapi/api/users/1 and it will call Get, however if I call myapi/api/users/authenticate?username={0}&password={1} then it will call Get (NOT Authenticate) and error:
我可以浏览到 myapi/api/users/ 它会调用 GetAll 我可以浏览到 myapi/api/users/1 它会调用 Get,但是如果我调用 myapi/api/users/authenticate?username={0} &password={1} 然后它会调用 Get (NOT Authenticate) 和错误:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
参数字典包含一个非空类型“System.Int32”的参数“id”的空条目,用于“Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController”中的方法“System.String Get(Int32)”。可选参数必须是引用类型、可为空类型或声明为可选参数。
How can I call custom method names such as Authenticate?
如何调用自定义方法名称,例如 Authenticate?
采纳答案by Darin Dimitrov
By default the route configuration follows RESTFul conventions meaning that it will accept only the Get, Post, Put and Delete action names (look at the route in global.asax => by default it doesn't allow you to specify any action name => it uses the HTTP verb to dispatch). So when you send a GET request to /api/users/authenticateyou are basically calling the Get(int id)action and passing id=authenticatewhich obviously crashes because your Get action expects an integer.
默认情况下,路由配置遵循 RESTFul 约定,这意味着它将只接受 Get、Post、Put 和 Delete 操作名称(查看 global.asax 中的路由 => 默认情况下它不允许您指定任何操作名称 =>它使用 HTTP 动词来调度)。因此,当您向您发送 GET 请求时,/api/users/authenticate您基本上是在调用该Get(int id)操作并传递id=authenticate该操作,这显然会崩溃,因为您的 Get 操作需要一个整数。
If you want to have different action names than the standard ones you could modify your route definition in global.asax:
如果您想要与标准操作名称不同的操作名称,您可以修改您的路由定义global.asax:
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
Now you can navigate to /api/values/getauthenticateto authenticate the user.
现在您可以导航到/api/values/getauthenticate对用户进行身份验证。
回答by Andrew
See this article for a longer discussion of named actions. It also shows that you can use the [HttpGet] attribute instead of prefixing the action name with "get".
有关命名操作的详细讨论,请参阅本文。它还表明您可以使用 [HttpGet] 属性而不是使用“get”作为操作名称的前缀。
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
回答by sky-dev
This is the best method I have come up with so far to incorporate extra GET methods while supporting the normal REST methods as well. Add the following routes to your WebApiConfig:
这是迄今为止我提出的最好的方法,可以在支持普通 REST 方法的同时合并额外的 GET 方法。将以下路由添加到您的 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)});
I verified this solution with the test class below. I was able to successfully hit each method in my controller below:
我用下面的测试类验证了这个解决方案。我能够成功地在我的控制器中点击下面的每个方法:
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)
{
}
}
I verified that it supports the following requests:
我确认它支持以下请求:
GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1
NoteThat if your extra GET actions do not begin with 'Get' you may want to add an HttpGet attribute to the method.
请注意,如果您的额外 GET 操作不以“Get”开头,您可能需要向该方法添加 HttpGet 属性。
回答by Kinjal Dixit
I am days into the MVC4 world.
我已经进入 MVC4 世界了。
For what its worth, I have a SitesAPIController, and I needed a custom method, that could be called like:
对于它的价值,我有一个 SitesAPIController,我需要一个自定义方法,可以这样调用:
http://localhost:9000/api/SitesAPI/Disposition/0
With different values for the last parameter to get record with different dispositions.
使用最后一个参数的不同值来获取具有不同处置的记录。
What Finally worked for me was:
最后对我有用的是:
The method in the SitesAPIController:
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;
}
And this in the WebApiConfig.cs
这在 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}"
);
For as long as I was naming the {disposition} as {id} i was encountering:
只要我将 {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."
}
When I renamed it to {disposition} it started working. So apparently the parameter name is matched with the value in the placeholder.
当我将它重命名为 {disposition} 时,它开始工作。所以显然参数名称与占位符中的值匹配。
Feel free to edit this answer to make it more accurate/explanatory.
随意编辑此答案以使其更准确/更具解释性。
回答by Nagaraju Mengani
Web Api by default expects URL in the form of api/{controller}/{id}, to override this default routing. you can set routing with any of below two ways.
默认情况下,Web Api 需要 api/{controller}/{id} 形式的 URL,以覆盖此默认路由。您可以使用以下两种方式中的任何一种来设置路由。
First option:
第一个选项:
Add below route registration in WebApiConfig.cs
在 WebApiConfig.cs 中添加以下路由注册
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Decorate your action method with HttpGet and parameters as below
使用 HttpGet 和参数装饰您的操作方法,如下所示
[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
string param2, string param3)
{
// your code here
}
for calling above method url will be like below
调用上面的方法 url 将如下所示
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1¶m2=value2¶m3=value3
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1¶m2=value2¶m3=value3
Second optionAdd route prefix to Controller class and Decorate your action method with HttpGet as below. In this case no need change any WebApiConfig.cs. It can have default routing.
第二个选项将路由前缀添加到 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
}
}
for calling above method url will be like below
调用上面的方法 url 将如下所示
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1¶m2=value2¶m3=value3
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1¶m2=value2¶m3=value3
回答by Darkseal
In case you're using ASP.NET 5with ASP.NET MVC 6, most of these answers simply won't work because you'll normally let MVC create the appropriate route collection for you (using the default RESTful conventions), meaning that you won't find any Routes.MapRoute()call to edit at will.
如果您将ASP.NET 5与ASP.NET MVC 6 一起使用,这些答案中的大多数都将不起作用,因为您通常会让 MVC 为您创建适当的路由集合(使用默认的 RESTful 约定),这意味着你不会找到任何随意Routes.MapRoute()编辑的电话。
The ConfigureServices()method invoked by the Startup.csfile will register MVC with the Dependency Injection framework built into ASP.NET 5: that way, when you call ApplicationBuilder.UseMvc()later in that class, the MVC framework will automatically add these default routes to your app. We can take a look of what happens behind the hood by looking at the UseMvc()method implementation within the framework source code:
该文件ConfigureServices()调用的方法Startup.cs将向 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());
}
The good thing about this is that the framework now handles all the hard work, iterating through all the Controller's Actions and setting up their default routes, thus saving you some redundant work.
这样做的好处是框架现在处理所有繁重的工作,迭代所有控制器的操作并设置它们的默认路由,从而为您节省一些多余的工作。
The bad thing is, there's little or no documentation about how you could add your own routes. Luckily enough, you can easily do that by using either a Convention-Basedand/or an Attribute-Basedapproach (aka Attribute Routing).
不好的是,关于如何添加自己的路由的文档很少或根本没有。幸运的是,您可以通过使用基于约定和/或基于属性的方法(又名属性路由)轻松地做到这一点。
Convention-Based
基于约定
In your Startup.cs class, replace this:
在您的 Startup.cs 类中,替换以下内容:
app.UseMvc();
with this:
有了这个:
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" }
);
});
Attribute-Based
基于属性
A great thing about MVC6 is that you can also define routes on a per-controller basis by decorating either the Controllerclass and/or the Actionmethods with the appropriate RouteAttributeand/or HttpGet/ HttpPosttemplate parameters, such as the following:
MVC6 的一个好处是,您还可以通过使用适当的和/或/模板参数装饰Controller类和/或Action方法,在每个控制器的基础上定义路由,如下所示:RouteAttributeHttpGetHttpPost
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!");
}
}
}
This controller will handle the following requests:
该控制器将处理以下请求:
[GET] api/items
[GET] api/items/5
[GET] api/items/GetLatestItems
[GET] api/items/GetLatestItems/5
[POST] api/items/PostSomething
Also notice that if you use the two approaches togheter, Attribute-based routes (when defined) would override Convention-based ones, and both of them would override the default routes defined by UseMvc().
另请注意,如果您同时使用这两种方法,基于属性的路由(定义时)将覆盖基于约定的路由,并且它们都将覆盖由UseMvc().
For more info, you can also read the following poston my blog.
回答by Dinuwan Kalubowila
Just modify your WebAPIConfig.cs as bellow
只需修改您的 WebAPIConfig.cs 如下
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional });
Then implement your API as bellow
然后按如下方式实现您的 API
// GET: api/Controller_Name/Show/1
[ActionName("Show")]
[HttpGet]
public EventPlanner Id(int id){}

