ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在从 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 (NOT Authenticate) 并报错:

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

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

请参考此链接:第 5 个答案stackoverflow.com/questions/12775590/…

O
OfirD

默认情况下,路由配置遵循 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 以验证用户。


有没有办法让它在仍然允许其他操作的同时仍然使用 Get(id)、Get() Put、Delete、Post?
@ShawnMclean 我猜你可以指定另一个没有 {action} 的路由,它对 {id} 有约束,这样 intGuid (或其他)以外的任何东西都不会匹配。那么它应该能够落入达林建议的那个
这里更重要的一点是,使用这种路由风格,您必须使用属性来指定允许的 HTTP 方法(如 [HttpGet])。
您确定需要使用其他操作吗?您是否真的尝试在 REST 约定中适应您正在做的事情?不必使用其他操作。
@niico:假设您想要一个 Count() 方法,该方法返回 Get() 将返回的元素数。我不知道如何将它放入 Get()、Get(id)、Post(...)、Put(...) 或 Delete(id)。而且,当然,我可以想象到更多可能的方法。
s
sky-dev

这是迄今为止我想出的最好的方法,可以合并额外的 GET 方法,同时也支持普通的 REST 方法。将以下路由添加到您的 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 属性。


很好的解决方案,你能告诉我如果我配置 putdelete 动词,就像你在 getpost 上所做的那样,也可以正常工作吗?
在我看来,这应该包含在 WebAPI 项目的默认值中(可能被注释掉)。它同时为您提供 WebAPI 和 MVC 风格的路由......
@FelipeOriani,我认为您不需要或不需要配置 putdelete 动词,因为这些请求通常会伴随一个 id 参数来标识您希望应用该操作的资源。对 /api/foodelete 调用应引发错误,因为您要删除哪个 foo?因此 DefaultApiWithId 路由应该可以很好地处理这些情况。
这根本不适合我。当我尝试执行基本 GET 时收到错误消息。
对于第一个 DefaultApiWithId,默认值不应该为 null 而不是 new { id = RouteParameter.Optional }?不需要'id'吗?
K
Kinjal Dixit

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

随意编辑这个答案,使其更准确/解释。


谢谢你的提示。我犯了和你一样的错误。
G
Glorfindel

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

第二个选项将路由前缀添加到控制器类并使用 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?param1=value1&param2=value2&param3=value3


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

如果您将 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());
}

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

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

基于约定

在您的 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 的一大优点是,您还可以通过使用适当的 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


太棒了!其他答案都没有真正做到我需要的。但是你救了我:)
有没有办法使用预定义的模型作为第二个参数?例如,当我像这样修补某个用户时:public IActionResult Patch(int id, [FromQuery] Person person)所有传入的属性都是空的!
A
Andrew

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

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


n
nzrytmn

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 中执行更多操作或更改某些内容。有关更多详细信息,您可以查看this article


D
Dinuwan Kalubowila

只需如下修改您的 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){}