Имя маршрута для HttpGet имя атрибута для базового универсального класса контроллера в asp.net ядро 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. Так что, похоже, shift на 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 С 2 параметрами. Эта версия принимает созданный объект для тела ответа, а не значения маршрута, что часто приводит к No route matches the supplied values ошибка. Если не хочешь возвращаться представление созданного ресурса в ответе, вы можете передать null как 3-й параметр:

    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 в самом деле.