ASP.NET Web API中的自定义方法名称


110

我正在从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(不进行身份验证)并报错:

参数字典包含用于'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'中方法'System.String Get(Int32)'的非空类型'System.Int32'的参数'id'的空条目。可选参数必须是引用类型,可为空的类型,或者必须声明为可选参数。

如何调用自定义方法名称,例如Authenticate?


5回答:请参考以下链接stackoverflow.com/questions/12775590/...
Vishwa摹

Answers:


136

默认情况下,路由配置遵循RESTFul约定,这意味着它将仅接受Get,Post,Put和Delete操作名称(默认情况下,请查看global.asax =>中的路由,不允许您指定任何操作名称=>它使用HTTP动词进行分派)。因此,当您向GET请求发送消息时,/api/users/authenticate您基本上是在调用该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/values/getauthenticate以验证用户身份。


20
有没有办法让它在仍然允许其他操作的同时仍然使用Get(id),Get()Put,Delete,Post?
肖恩·麦克林

@ShawnMclean我想您可以在没有{action}限制的情况下指定其他路由,{id}以便除intGuid(或其他)之外的任何其他都不匹配。然后,它应该可以落
入达林

2
此处更重要的一点是,使用这种路由样式,必须使用属性来指定允许的HTTP方法(例如[HttpGet])。
喜马拉雅山Garg

1
您确定需要使用其他操作吗?您是否真的尝试过将自己的工作适合REST约定?不必使用其他操作。
niico

1
@niico:假设您想拥有一个Count()方法,该方法返回Get()将返回的元素数量。我没有看到如何使它适合Get(),Get(id),Post(...),Put(...)或Delete(id)。当然,还有我可以想象的更多不确定的方法。
詹斯·曼德

88

到目前为止,这是我想出的最好的方法,可以在支持常规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属性。


1
不错的解决方案,您能告诉我,如果我像在和上一样配置putdelete动词,也可以正常工作吗?getpost
Felipe Oriani 2013年

1
在我看来,这应该包含在WebAPI项目的默认值中(可能已注释掉)。它同时为您提供了WebAPI和MVC样式的路由……
John Culviner

1
@FelipeOriani,我认为您不需要或不需要配置putdelete动词,因为这些请求通常会伴随id参数来标识您希望对该操作应用的资源。一个delete调用/api/foo应该扔掉,因为它foo的是你想删除的错误?因此,DefaultApiWithId路由应该可以很好地处理这些情况。
2013年

4
这对我根本没有用。尝试执行基本GET时收到错误消息。
马特

对于第一个,DefaultApiWithId,默认值是否应该为null而不是new {id = RouteParameter.Optional}?不需要'id'吗?
约翰尼·奥希卡

22

我已经进入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}时,它开始工作。因此,显然参数名与占位符中的值匹配。

随意编辑此答案,使其更加准确/说明。


谢谢你的提示。我和你犯了同样的错误。
2014年

16

默认情况下,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:[您的端口] / api / MyData / ReadMyData?param1 = value1&param2 = value2&param3 = 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:[您的端口] / api / MyData / ReadMyData?param1 = value1&param2 = value2&param3 = value3


我非常喜欢第二种选择。您还能告诉我如何在VB.net中使用它吗?非常感谢。
user1617676

12

如果您将ASP.NET 5ASP.NET MVC 6一起使用,则大多数答案都将根本无效,因为您通常会让MVC为您创建适当的路由集合(使用默认的RESTful约定),这意味着您将找不到任何可以随意Routes.MapRoute()编辑的电话。

文件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());
}

这样做的好处是,框架现在可以处理所有艰苦的工作,遍历所有Controller的Action并设置其默认路由,从而节省了一些多余的工作。

不好的是,关于如何添加自己的路线的文档很少甚至没有。幸运的是,您可以使用基于约定的和/或基于属性的方法(也称为“ 属性路由”)轻松地做到这一点。

基于公约

在您的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的一大优点是,您还可以通过使用适当的和/或/ 模板参数修饰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!");
        }
    }
}

该控制器将处理以下请求:

 [GET] api/items
 [GET] api/items/5
 [GET] api/items/GetLatestItems
 [GET] api/items/GetLatestItems/5
 [POST] api/items/PostSomething

另请注意,如果您同时使用两种方法,基于属性的路由(定义时)将覆盖基于约定的路由,而这两个路由均将覆盖由定义的默认路由UseMvc()

有关更多信息,您还可以在我的博客上阅读以下文章


1
太棒了!其他答案实际上都没有满足我的要求。但是你救了我:)
亚瑟王3

有没有办法使用预定义的模型作为第二个参数?例如,当我像这样修补某个用户时: public IActionResult Patch(int id, [FromQuery] Person person)所有传入的属性都为null!
亚瑟王3


0

只需将您的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){}

0

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中做更多的事情或更改任何内容。有关更多详细信息,请查看本文

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.