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

C# ASP.NET Web API中的自定义方法名称,c#,asp.net-web-api,asp.net-web-api-routing,C#,Asp.net Web Api,Asp.net Web Api Routing,我正在从WCF Web API转换为新的ASP.NET MVC 4 Web API。我有一个UsersController,我想要一个名为Authenticate的方法。我看到了如何执行GetAll、GetOne、Post和Delete的示例,但是如果我想在这些服务中添加额外的方法呢?例如,我的UsersService应该有一个名为Authenticate的方法,在其中传递用户名和密码,但是它不起作用 public class UsersController : BaseApiControlle

我正在从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(而不是authenticate)并出错:

参数字典包含“Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController”中方法“System.String Get(Int32)”的不可为null类型“System.Int32”的参数“id”的null条目。可选参数必须是引用类型、可为null的类型或声明为可选参数


如何调用自定义方法名,如身份验证?

默认情况下,路由配置遵循RESTFul约定,这意味着它将只接受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”


这是迄今为止我提出的最好的方法,可以在支持正常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] api/items
 [GET] api/items/5
 [GET] api/items/GetLatestItems
 [GET] api/items/GetLatestItems/5
 [POST] api/items/PostSomething

请注意,如果额外的GET操作不是以“GET”开头,则可能需要向该方法添加一个HttpGet属性。

我已进入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}"
 );
config.Routes.MapHttpRoute(
    name: "CustomApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);
当我将{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}时,它开始工作。显然,参数名与占位符中的值匹配


请随意编辑此答案,以使其更准确/更具解释性。

默认情况下,Web Api需要Api/{controller}/{id}形式的URL来覆盖此默认路由。您可以使用以下两种方法中的任意一种设置路由

第一个选项:

在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}"
 );
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如下所示

第二选项 将路由前缀添加到控制器类,并使用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如下所示


如果您将ASP.NET 5ASP.NET MVC 6一起使用,这些答案中的大多数根本不起作用,因为您通常会让MVC为您创建适当的路由集合(使用默认的RESTful约定),这意味着您不会随意找到任何
路由。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());
}
为此:

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!");
        }
    }
}
还请注意,如果同时使用这两种方法,则基于属性的路由(定义时)将覆盖基于约定的路由,并且这两种路由都将覆盖由
UseMvc()
定义的默认路由


有关更多信息,您也可以访问我的博客。

只需修改您的WebAPIConfig.cs,如下所示

Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}/{action}/{id}",
  defaults: new { action = "get", id = RouteParameter.Optional });
    // GET: api/Controller_Name/Show/1
    [ActionName("Show")]
    [HttpGet]
    public EventPlanner Id(int id){}
然后实现如下所示的API

Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}/{action}/{id}",
  defaults: new { action = "get", id = RouteParameter.Optional });
    // 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中执行更多操作或更改某些内容。有关更多详细信息,您可以查看。

有没有办法让它在仍然允许其他操作的情况下仍然使用get(id)、get()Put、Delete、Post?@ShawnMclea