Custom method names in ASP.NET Web API

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.

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:

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.

How can I call custom method names such as Authenticate?

177832 次浏览

默认情况下,路由配置遵循 RESTFull 约定,这意味着它将只接受 Get、 Post、 Put 和 Delete 操作名称(查看 global.asax = > 中的路由,默认情况下,它不允许指定任何操作名称 = > ,它使用 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来验证用户。

有关命名操作的更详细讨论,请参阅本文。它还显示您可以使用[ HttpGet ]属性,而不是在操作名称前面加上“ get”。

Http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

到目前为止,这是我想到的最好的方法,可以在支持普通 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)});

我用下面的测试类验证了这个解决方案。我能够成功地点击下面我的控制器中的每个方法:

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 属性。

我已经进入 MVC4世界好几天了。

不管怎样,我有一个 SitesAPI 控制器,我需要一个定制的方法,可以这样调用:

http://localhost:9000/api/SitesAPI/Disposition/0

对于最后一个参数,使用不同的值来获取具有不同配置的记录。

What Finally worked for me was:

SitesAPI 控制器中的方法:

// 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}"
);

只要我把{性情}命名为{ 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."
}

当我把它重命名为{ position }时,它就开始工作了。显然,参数名与占位符中的值相匹配。

Feel free to edit this answer to make it more accurate/explanatory.

默认情况下,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


}

for calling above method url will be like below

Http://localhost : [ yourport ]/api/MyData/ReadMyData

第二个选择 向 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

如果你使用 ASP.NET 5ASP.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());
}

这样做的好处是,框架现在可以处理所有繁重的工作,迭代控制器的所有操作并设置它们的默认路由,从而为您节省了一些冗余的工作。

糟糕的是,关于如何添加自己的路线,几乎没有或根本没有文档说明。幸运的是,您可以通过使用 Convention-Based和/或 基于属性的方法(即 属性路由)轻松实现这一点。

基于惯例

In your Startup.cs class, replace this:

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" }
);
});

基于属性的

MVC6的一个好处是,你也可以通过使用适当的 RouteAttribute和/或 HttpGet/HttpPost模板参数来装饰 Controller类和/或 Action方法,从而在每个控制器的基础上定义路由,例如:

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

只需修改 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){}

WebAPi2和更高版本支持一种新的路由类型,称为属性路由。顾名思义,属性路由使用属性来定义路由。属性路由使您能够更好地控制 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 中做更多的事情或更改某些内容。更多细节,你可以看看 这篇文章