Имена пользовательских методов в ASP.NET Web API - PullRequest
106 голосов
/ 05 марта 2012

Я преобразую веб-API WCF в новый веб-API ASP.NET MVC 4. У меня есть 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} & пароль = {1}, тогда он вызовет Get (NOT Authenticate) и ошибку:

Словарь параметров содержит пустую запись для параметра 'id' ненулевого типа 'System.Int32' для метода 'System.String Get (Int32)' в 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController' , Необязательный параметр должен быть ссылочным типом, обнуляемым типом или быть объявлен как необязательный параметр.

Как я могу вызвать имена пользовательских методов, таких как Authenticate?

Ответы [ 6 ]

127 голосов
/ 05 марта 2012

По умолчанию конфигурация маршрута соответствует соглашениям RESTFul, что означает, что он будет принимать только имена действий Get, Post, Put и Delete (посмотрите маршрут в global.asax =>, по умолчанию он не позволяет указывать какие-либо действия name => он использует глагол 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 для аутентификации пользователя.

85 голосов
/ 29 августа 2012

Это лучший метод, который я когда-либо придумывал, чтобы включить дополнительные методы 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.

18 голосов
/ 30 августа 2013

Я уже несколько дней в мире 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}, он начал работать. Таким образом, очевидно, что имя параметра совпадает со значением в заполнителе.

Не стесняйтесь редактировать этот ответ, чтобы сделать его более точным / пояснительным.

14 голосов
/ 22 июля 2015

Web Api по умолчанию ожидает URL в форме api / {controller} / {id}, чтобы переопределить эту маршрутизацию по умолчанию.Вы можете установить маршрутизацию любым из двух способов:

Первый вариант:

Добавить ниже регистрацию маршрута в 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

}

для вызова вышеупомянутого метода, будет выглядеть следующим образом

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

10 голосов
/ 14 февраля 2016

В случае, если вы используете ASP.NET 5 с ASP.NET MVC 6 , большинство из этих ответов просто не будут работать, потому что вы обычно позволяете MVC создавать соответствующий набор маршрутов для вас (с использованием соглашений RESTful по умолчанию), что означает, что вы не найдете ни одного Routes.MapRoute() вызова для редактирования по желанию.

Метод ConfigureServices(), вызванный файлом Startup.cs, зарегистрирует MVC с интегрированной средой Dependency Injection, встроенной в ASP.NET 5: таким образом, когда вы вызовете 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 является то, что вы также можете определять маршруты для каждого контроллера, декорируя класс Controller и / или методы Action соответствующими RouteAttribute и / или HttpGet / HttpPost параметры шаблона, такие как:

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().

Для получения дополнительной информации, вы также можете прочитать следующий пост в моем блоге.

9 голосов
/ 08 марта 2012

См. Эту статью для более подробного обсуждения именованных действий.Это также показывает, что вы можете использовать атрибут [HttpGet] вместо префикса имени действия с помощью «get».

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

...