Имя маршрута для атрибута HttpGet Имя базового класса контроллера класса в asp.net core 2

У меня есть общий контроллер, который имеет несколько производных классов контроллера. но я не могу понять, как обрабатывать имя маршрута HttpGet , так как он требует постоянной.

[HttpGet("{id}", Name ="should not hard coded here for derived class")]
 public virtual async Task<IActionResult> Get(int id)

Мне нужно имя маршрута, потому что в моей функции HttpPost я хочу вернуть CreatedAtRoute(), для которых требуется HttpGet имя маршрута

Имя маршрута не может быть жестко закодировано, потому что у всех производных классов должно быть другое имя маршрута.

вот базовый контроллер

public abstract class BaseController<TEntity, TContext> : Controller where TEntity : BaseOptionType, new() where TContext : DbContext
{
    private readonly IGenericRepository<TEntity, TContext> _repository;
    private readonly ILogger<BaseGenericOptionTypesController<TEntity, TContext>> _logger;
    public BaseController(IGenericRepository<TEntity, TContext> repository, ILogger<BaseController<TEntity, TContext>> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{id}", Name = "should not hard code here for derived class")]
    public virtual async Task<IActionResult> Get(int id)
    {
        var optionType = await _repository.FindByIdAsync(id);
        if (optionType == null)
        {
            _logger.LogInformation($"[ID not found]");
            return NotFound();
        }
        return Ok(optionType);
    }
}

Вот производный контроллер

[Route("api/v1/DerivedControllerA")]
public class DerivedControllerA : BaseController<TimeOff, HRContext>
{
    public DerivedControllerA(IGenericRepository<TimeOff, HRContext> repository, ILogger<DerivedControllerA> logger)
        : base(repository, logger)
    {

    }
}  

Любая помощь будет оценена, спасибо.

Ответ 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.