首页 文章

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

提问于
浏览
104

我正在从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)和错误:

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

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

6 回答

  • 18

    有关命名操作的详细讨论,请参阅此文章 . 它还显示您可以使用[HttpGet]属性,而不是使用“get”为操作名称添加前缀 .

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

  • 83

    如果您使用 ASP.NET 5ASP.NET MVC 6 ,大多数答案只是赢了't work because you' ll通常让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());
    }
    

    关于这一点的好处是框架现在可以处理所有的艰苦工作,遍历所有Controller的操作并设置其默认路由,从而为您节省一些冗余工作 .

    不好的是,关于如何添加自己的路线的文档很少或根本没有 . 幸运的是,您可以通过使用 Convention-Based 和/或 Attribute-Based 方法(也称为属性路由)轻松完成此操作 .

    Convention-Based

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

    Attribute-Based

    关于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 .

  • 124

    默认情况下,路由配置遵循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/values/getauthenticate 以对用户进行身份验证 .

  • 7

    这是迄今为止我提出的最佳方法,即在支持常规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属性 .

  • 9

    我是进入MVC4世界的日子 .

    为了它的 Value ,我有一个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}"
     );
    

    只要我将命名为我遇到了:

    {
    "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."
    }
    

    当我将它重命名为时,它开始工作了 . 显然,参数名称与占位符中的值匹配 .

    随意编辑此答案,使其更准确/解释 .

  • 14

    默认情况下,Web Api需要api / / 形式的URL来覆盖此默认路由 . 您可以使用以下两种方式之一设置路由 .

    First option:

    在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

    Second option 将路由前缀添加到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?param1=value1&param2=value2&param3=value3

相关问题