Маршрутизация веб-API с несколькими параметрами

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

public class MyController : ApiController
{
    // POST api/MyController/GetAllRows/userName/tableName
    [HttpPost]
    public List<MyRows> GetAllRows(string userName, string tableName)
    {
        ...
    }

    // POST api/MyController/GetRowsOfType/userName/tableName/rowType
    [HttpPost]
    public List<MyRows> GetRowsOfType(string userName, string tableName, string rowType)
    {
        ...
    }
}

На данный момент я использую эту маршрутизацию для URL-адресов:

routes.MapHttpRoute("AllRows", "api/{controller}/{action}/{userName}/{tableName}",
                    new
                    {
                        userName= UrlParameter.Optional,
                        tableName = UrlParameter.Optional
                    });

routes.MapHttpRoute("RowsByType", "api/{controller}/{action}/{userName}/{tableName}/{rowType}",
                    new
                    {
                        userName= UrlParameter.Optional,
                        tableName = UrlParameter.Optional,
                        rowType= UrlParameter.Optional
                    });

но на данный момент работает только первый метод (с двумя параметрами). Я нахожусь на правильных строках, или я получил неправильный формат URL или маршрутизацию? Маршрутизация кажется мне черной магией...

Ответы

Ответ 1

Проблема заключается в том, что ваш URL api/MyController/GetRowsOfType/userName/tableName/rowType всегда будет соответствовать первому маршруту, поэтому вторая никогда не будет достигнута.

Просто исправить, сначала зарегистрируйте маршрут RowsByType.

Ответ 2

Я видел, что WebApiConfig получает "из-под контроля" с сотнями маршрутов, размещенными в нем.

Вместо этого я лично предпочитаю Маршрутизация атрибутов

Вы вводите его в замешательство с помощью POST и GET

[HttpPost]
public List<MyRows> GetAllRows(string userName, string tableName)
{
   ...
}

HttpPost И GetAllRows?

Почему бы не сделать это:

[Route("GetAllRows/{user}/{table}")]
public List<MyRows> GetAllRows(string userName, string tableName)
{
   ...
}

ИЛИ изменить маршрут ( "PostAllRows" и "PostRows" ). Я думаю, что вы действительно выполняете запрос GET, поэтому код, который я показываю, должен работать на вас. Ваш звонок от клиента будет WHATEVER находится в ROUTE, поэтому он НАЙДЕТ METHOD с GetAllRows, но сам метод, это имя МОЖЕТ БЫТЬ все, что вы хотите, до тех пор, пока вызывающий объект соответствует URL-адресу в ROUTE, вы можете добавить GetMyStuff для этого метода, если вы действительно этого хотели.

Update:

Я предпочитаю быть explicit с типом HTTP methods И я предпочитаю сопоставлять параметры маршрута параметрам метода

[HttpPost]
[Route("api/lead/{vendorNumber}/{recordLocator}")]
public IHttpActionResult GetLead(string vendorNumber, string recordLocator)
{ .... }

(маршрут lead не должен совпадать с именем метода GetLead, но вы захотите сохранить те же имена в параметрах маршрута и параметрах метода, даже если вы можете изменить порядок, например, поставить recordLocator до того, как vendorNumber если маршрут противоположный - я этого не делаю, потому что почему более запутанно смотреть на него).

Bonus: Теперь вы всегда можете использовать регулярное выражение в маршрутах, например

[Route("api/utilities/{vendorId:int}/{utilityType:regex(^(?i)(Gas)|(Electric)$)}/{accountType:regex(^(?i)(Residential)|(Business)$)}")]
public IHttpActionResult GetUtilityList(int vendorId, string utilityType, string accountType)
    {