Ответ 1
Я не буду спорить с NightOwl888 об использовании базовых контроллеров в MVC. Есть плюсы и минусы, и я рассматривал проекты, в которых использование базовых контроллеров было оправданным.
Что касается оригинального вопроса, кажется, что самый простой способ обойти эту проблему - использовать CreatedAtAction
вместо CreatedAtRoute
. CreatedAtAction
не требует, чтобы вы назовете ваши маршруты, вы можете просто использовать Get
имя действия из базового контроллера. Если CreatedAtAction
вызывается из DerivedControllerA
, он выдает URL-адрес для действия Get
в DerivedControllerA
, и если он вызывается из DerivedControllerB
, он выдает URL-адрес для действия Get
в DerivedControllerB
. Кажется, что переход на CreatedAtAction
довольно хорошо охватывает ваш прецедент.
Вот пример вызова CreatedAtAction
:
[HttpPost]
public virtual IActionResult Post(/* ... */)
{
// Create and save an instance in repository
// var createdObject = ...;
return CreatedAtAction(nameof(Get), new
{
// Put actual id here
id = 123
}, createdObject);
}
Общей ошибкой является вызов перегрузки CreatedAtAction
с двумя параметрами. Эта версия принимает созданный объект для тела ответа, а не значения маршрута, что часто приводит к ошибке No route matches the supplied values
. Если вы не хотите возвращать представление созданного ресурса в ответе, вы можете передать null
в качестве третьего параметра:
return CreatedAtAction(nameof(Get), new
{
// Put actual id here
id = 123
}, null);
Если по какой-то причине вы хотите придерживаться вызова CreatedAtRoute
, единственное возможное решение, которое приходит мне на ум, - иметь разные действия в каждом производном классе, который просто вызывает базовый метод с фактической логикой:
[Route("api/v1/DerivedControllerA")]
public class DerivedControllerA : BaseController<TimeOff, HRContext>
{
// ...
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[HttpGet("{id}", Name = "RouteForDerivedControllerA")]
public virtual Task<IActionResult> Get(int id)
{
return base.Get(id);
}
}
public abstract class BaseController<TEntity, TContext> : Controller where TEntity : BaseOptionType, new() where TContext : DbContext
{
// ...
public virtual async Task<IActionResult> Get(int id)
{
// Actual logic goes here
}
}
Такое решение, однако, фактически обесценивает использование BaseController
.