Імена спеціальних методів у веб-API ASP.NET


110

Я перетворюю з веб-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 / authentication? Ім'я користувача = {0} & password = {1}, тоді він зателефонує Get (NOT Authenticate) та помилка:

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

Як я можу викликати імена спеціальних методів, такі як Authenticate?


Перейдіть за цим посиланням: 5-та відповідь stackoverflow.com/questions/12775590/…
Vishwa G

Відповіді:


137

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


20
Чи існує спосіб, як це все-таки використовувати Get (id), Get () Покласти, Видалити, Опублікувати, при цьому все ще дозволяючи інші дії?
Шон Мклін

@ShawnMclean Я думаю, що ви можете вказати інший маршрут, без {action}якого є обмеження, {id}щоб нічого, крім intабо Guid(або будь-якого іншого), не відповідало. Тоді він повинен мати можливість перейти до тієї, яку запропонував Дарін
Стів Грейрекс

2
Ще одна важлива річ тут полягає в тому, що для цього стилю маршрутизації ви повинні використовувати атрибути для вказівки дозволених методів HTTP (наприклад, [HttpGet]).
Himalaya Garg

1
ви впевнені, що вам потрібно використовувати інші дії? Ви справді намагалися відповідати тому, що ви робите, в рамках конвенцій REST? Не слід використовувати інших дій.
niico

1
@niico: Уявіть, що ви хотіли мати метод Count (), який повертає кількість елементів Get (), які повернуться. Я не бачу, як це вписати в Get (), Get (id), Post (...), Put (...) або Delete (id). І, звичайно, існує нескінченно більше можливих методів, які я міг би собі уявити.
Йенс Мандер

88

Це найкращий метод, який я придумав до цих пір, щоб включити додаткові методи 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.


1
приємне рішення, чи не могли б ви сказати мені, якщо я конфігурую putі deleteтакі дієслова, як у вас, getі postчи добре працюватиме?
Феліпе Оріані

1
На мою думку, це має бути включено до стандартних проектів WebAPI (можливо, прокоментовано). Це дає вам маршрути в стилі WebAPI та MVC одночасно ...
Джон Culviner

1
@FelipeOriani, я не думаю, що ви хочете або потребувати налаштування putчи deleteдієслів, оскільки ці запити зазвичай супроводжують параметр id для ідентифікації ресурсу, до якого ви хочете застосувати цю операцію. deleteВиклик /api/fooповинен видавати помилку , тому що Foo ви намагаєтеся видалити? Тому маршрут DefaultApiWithId повинен добре справлятися з цими випадками.
nwayve

4
це для мене взагалі не вийшло. я отримав повідомлення про помилки, коли я намагався зробити базовий GET.
Метт

У першому, DefaultApiWithId, чи не повинні значення за замовчуванням бути нульовими замість нових {id = RouteParameter.O optional}? Чи не потрібен "id"?
Джонні Ошика

22

Я днями у світ 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}, він почав працювати. Тому, мабуть, назва параметра збігається зі значенням у заповнювачі.

Не соромтесь редагувати цю відповідь, щоб зробити її більш точною / пояснювальною.


Дякую за пораду. Я робив ту саму помилку, що і ви.
abhi

16

За замовчуванням 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

}

для виклику вище методу URL буде, як нижче

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


Другий варіант мені дуже подобається. Чи можете ви також показати мені, як ним користуватися на VB.net? Дуже дякую.
користувач1617676

12

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

ConfigureServices()Метод викликається Startup.csфайл зареєструє MVC з рамками впровадження залежностей , вбудованої в 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().

Для отримання додаткової інформації ви також можете прочитати наступний пост у моєму блозі.


1
Це прекрасно! Жодна з інших відповідей насправді не робила того, що мені потрібно. Але ти мене врятував :)
Король Артур Третє

Чи існує спосіб використання попередньо визначеної моделі як другого параметра? Наприклад, коли я латки певного користувача , як це: public IActionResult Patch(int id, [FromQuery] Person person), всі вхідні властивості рівне нуль!
Король Артур ІІІ


0

Просто змініть ваш 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){}

0

Веб-версії APi 2 та новіших версій підтримують новий тип маршрутизації, який називається маршрутизацією атрибутів. Як випливає з назви, маршрутизація атрибутів використовує атрибути для визначення маршрутів. Маршрутизація атрибутів дає вам більше контролю над URI-адресами вашого веб-API. Наприклад, ви можете легко створити URI, які описують ієрархію ресурсів.

Наприклад:

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

Буде ідеально, і вам не потрібен додатковий код, наприклад, на WebApiConfig.cs. Просто ви повинні бути впевнені, що веб-маршрутна маршрутизація включена чи не в WebApiConfig.cs, якщо ні, ви не можете активувати, як показано нижче:

        // Web API routes
        config.MapHttpAttributeRoutes();

Вам не потрібно робити щось більше або щось змінювати в WebApiConfig.cs. Більш детально ви можете ознайомитись із цією статтею .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.