Я использую маршрутизацию атрибутов Web API 2 в своем проекте, чтобы предоставить интерфейс JSON для моих данных. Столкнулся со странным поведением при выборе контроллера, пока не решил, баг это или фича :) Опишу свой подход.
Я хотел бы имитировать синтаксис OData с помощью маршрутизации атрибутов (прямое использование OData было отклонено из-за принципов проектирования). Например, чтобы получить сущность с id=5, я использую HTTP-запрос GET к URI http://mydomain.com/api/Entity(5) . Я ожидаю использовать тот же URI с глаголом HTTP PUT для обновления объекта. Здесь начинается путешествие...
Я хотел бы иметь отдельный контроллер для получения сущностей (FirstController
в приведенном ниже примере) и еще один для изменения сущностей (SecondController
). Оба контроллера обрабатывают один и тот же URI (например, http://mydomain.com/api/Entity(5)) единственная разница заключается в том, что HTTP-глагол используется с URI - GET должен обрабатываться FirstController
, PUT должен обрабатываться SecondController
. Но URI не обрабатывается ни одним из них; вместо этого возвращается ошибка HTTP 404. Когда я «объединяю» действия GET и PUT только с одним контроллером (закомментировано в FirstController
), оба глагола обрабатываются правильно. Я использую IIS Express, и все обычные маршруты отключены, отвечает только маршрутизация атрибутов.
Похоже, что процесс выбора контроллера не работает с глаголом HTTP. Другими словами, атрибуты HttpGet
и HttpPut
просто ограничивают использование действий, но они не служат критериями при выборе контроллера. Я не очень хорошо знаком с основами MVC/Web API, поэтому позвольте мне задать вам большой вопрос:
Является ли поведение, описанное здесь ранее, функцией, намеренно реализованной MVC/Web API 2, или ошибкой, которую необходимо исправить?
Если это рассматривать как особенность, это мешает мне следовать принципам дизайна. Я могу жить с «объединенными» контроллерами, но все же считаю это плохой практикой... Или я что-то упускаю из виду?
Моя настройка среды:
- Windows 7 (виртуальная машина с использованием Oracle VirtualBox)
- Визуальная студия 2013
- .NET 4.5.1
- Веб-API 2
Ниже приведена реализация класса FirstController
:
public class FirstController : ApiController
{
[HttpGet]
[Route("api/Entity({id:int})")]
public Output GetEntity(int id)
{
Output output = new Output() { Id = id, Name = "foo" };
return output;
}
//[HttpPut]
//[Route("api/Entity({id:int})")]
//public Output UpdateEntity(int id, UpdateEntity command)
//{
// Output output = new Output() { Id = id, Name = command.Name };
// return output;
//}
}
Ниже приведена реализация класса SecondController
:
public class SecondController : ApiController
{
[HttpPut]
[Route("api/Entity({id:int})")]
public Output UpdateEntity(int id, UpdateEntity command)
{
Output output = new Output() { Id = id, Name = command.Name };
return output;
}
}
Ниже приведена реализация консольного приложения для проверки описанного поведения:
class Program
{
static void Main(string[] args)
{
// HTTP client initialization
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:1567");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET - FirstController.GetEntity
HttpResponseMessage getEntityResponse = httpClient.GetAsync("/api/Entity(5)").Result;
Output getOutput = getEntityResponse.Content.ReadAsAsync<Output>().Result;
// HTTP PUT - SecondController.UpdateEntity
UpdateEntity updateCommand = new UpdateEntity() { Name = "newEntityname" };
HttpResponseMessage updateEntityResponse = httpClient.PutAsJsonAsync("/api/Entity(10)", updateCommand).Result;
Output updateOutput = updateEntityResponse.Content.ReadAsAsync<Output>().Result;
}
}
Для завершения используются следующие DTO:
public class UpdateEntity
{
public string Name { get; set; }
}
public class Output
{
public int Id { get; set; }
public string Name { get; set; }
}
Заранее спасибо за ваши ответы,
Ян Качина