asp.net 核心 2 中基本通用控制器 class 的 HttpGet 属性名称的路由名称

Route Name for HttpGet attribute Name for base generic controller class in asp.net core 2

我有一个通用控制器,它有几个派生控制器 classes。但我不知道如何处理 HttpGet 的 路由名称 因为它需要常量。

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

我需要路由名称,因为在我的 HttpPost 函数中我想 return CreatedAtRoute() 需要 HttpGet 的 路由名称

路由名称不能硬编码,因为所有派生的 class 都需要有不同的路由名称。

这里是基础控制器

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)
    {

    }
}  

任何帮助将不胜感激,谢谢。

我不会与 NightOwl888 争论在 MVC 中使用基本控制器。有利有弊,我已经处理过使用基本控制器是合理的项目。

关于原来的问题,解决这个问题的最简单方法似乎是使用 CreatedAtAction 而不是 CreatedAtRouteCreatedAtAction 不需要你命名你的路线,你可以只使用来自基本控制器的 Get 动作名称。如果从 DerivedControllerA 调用 CreatedAtAction,它将在 DerivedControllerA 中为 Get 操作生成 URL,如果从 DerivedControllerB 调用,它将为 DerivedControllerB 中的 Get 操作生成 URL。所以似乎转向 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);
}

常见的错误是用 2 个参数调用 CreatedAtAction 的重载。此版本将创建的对象作为响应主体,而不是路由值,这通常会导致 No route matches the supplied values 错误。如果您不想在响应中 return 表示创建的资源,您可以将 null 作为第三个参数传递:

    return CreatedAtAction(nameof(Get), new
    {
        //  Put actual id here
        id = 123
    }, null);

如果出于某种原因你想坚持使用 CreatedAtRoute 调用,我想到的唯一可能的解决方案是在每个派生的 class 中有不同的操作,它只调用基本方法实际逻辑:

[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 的使用。