简体   繁体   English

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

[英]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.我正在从 WCF Web API 转换为新的 ASP.NET MVC 4 Web API。 I have a UsersController, and I want to have a method named Authenticate.我有一个 UsersController,我想要一个名为 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?我看到了如何执行 GetAll、GetOne、Post 和 Delete 的示例,但是如果我想在这些服务中添加额外的方法怎么办? For instance, my UsersService should have a method called Authenticate where they pass in a username and password, however it doesn't work.例如,我的 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'.参数字典包含“Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController”中方法“System.String Get(Int32)”的不可为空类型“System.Int32”的参数“id”的空条目。 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?如何调用自定义方法名称,例如 Authenticate?

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).默认情况下,路由配置遵循 RESTFul 约定,这意味着它将只接受 Get、Post、Put 和 Delete 操作名称(查看 global.asax 中的路由 => 默认情况下它不允许您指定任何操作名称 =>它使用 HTTP 动词来调度)。 So when you send a GET request to /api/users/authenticate you are basically calling the Get(int id) action and passing id=authenticate which obviously crashes because your Get action expects an integer.因此,当您向/api/users/authenticate发送 GET 请求时,您基本上是在调用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/users/getauthenticate to authenticate the user.现在您可以导航到/api/users/getauthenticate对用户进行身份验证。

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.这是迄今为止我想出的最好的方法,可以在支持普通 REST 方法的同时合并额外的 GET 方法。 Add the following routes to your WebApiConfig:将以下路由添加到您的 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

Note That if your extra GET actions do not begin with 'Get' you may want to add an HttpGet attribute to the method.请注意,如果您的额外 GET 操作不以“Get”开头,您可能需要向该方法添加 HttpGet 属性。

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.当我将它重命名为 {disposition} 时,它开始工作。 So apparently the parameter name is matched with the value in the placeholder.所以显然参数名称与占位符中的值匹配。

Feel free to edit this answer to make it more accurate/explanatory.随意编辑此答案以使其更准确/更具解释性。

Web Api by default expects URL in the form of api/{controller}/{id}, to override this default routing.默认情况下,Web Api 需要 api/{controller}/{id} 形式的 URL,以覆盖此默认路由。 you can set routing with any of below two ways.您可以使用以下两种方式中的任何一种来设置路由。

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&param2=value2&param3=value3 http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1&param2=value2&param3=value3

Second option Add route prefix to Controller class and Decorate your action method with HttpGet as below.第二个选项将路由前缀添加到 Controller 类并使用 HttpGet 装饰您的操作方法,如下所示。 In this case no need change any WebApiConfig.cs.在这种情况下,无需更改任何 WebApiConfig.cs。 It can have default routing.它可以有默认路由。

[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&param2=value2&param3=value3 http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1&param2=value2&param3=value3

In case you're using ASP.NET 5 with 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 5ASP.NET MVC 6 一起使用,这些答案中的大多数都将不起作用,因为您通常会让 MVC 为您创建适当的路由集合(使用默认的 RESTful 约定),这意味着你不会找到任何Routes.MapRoute()调用来Routes.MapRoute()编辑。

The ConfigureServices() method invoked by the Startup.cs file 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. Startup.cs文件调用的ConfigureServices()方法将向 ASP.NET 5 中内置的依赖注入框架注册 MVC:这样,当您稍后在该类中调用ApplicationBuilder.UseMvc() ,MVC 框架将自动添加这些到您的应用程序的默认路由。 We can take a look of what happens behind the hood by looking at the UseMvc() method implementation within the framework source code:我们可以通过查看框架源代码中的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-Based and/or an Attribute-Based approach (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 Controller class and/or the Action methods with the appropriate RouteAttribute and/or HttpGet / HttpPost template parameters, such as the following: 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!");
        }
    }
}

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 post on my blog.有关更多信息,您还可以阅读我博客上的以下帖子

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

Web APi 2 and later versions support a new type of routing, called attribute routing. Web APi 2 及更高版本支持一种新型路由,称为属性路由。 As the name implies, attribute routing uses attributes to define routes.顾名思义,属性路由使用属性来定义路由。 Attribute routing gives you more control over the URIs in your web API.属性路由使您可以更好地控制 Web API 中的 URI。 For example, you can easily create URIs that describe hierarchies of resources.例如,您可以轻松创建描述资源层次结构的 URI。

For example:例如:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }

Will perfect and you don't need any extra code for example in WebApiConfig.cs.将完美,您不需要任何额外的代码,例如在 WebApiConfig.cs 中。 Just you have to be sure web api routing is enabled or not in WebApiConfig.cs , if not you can activate like below:只是您必须确保在 WebApiConfig.cs 中启用了 web api 路由,如果没有,您可以像下面那样激活:

        // Web API routes
        config.MapHttpAttributeRoutes();

You don't have to do something more or change something in WebApiConfig.cs.您不必在 WebApiConfig.cs 中执行更多操作或更改某些内容。 For more details you can have a look this article .有关更多详细信息,您可以查看这篇文章

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){}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM