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

HEx*_*xit 14 c# asp.net-core-mvc asp.net-core-webapi asp.net-core-routing

我有一个通用控制器,它有几个派生的控制器类.但我无法弄清楚如何处理HttpGet的路由名称,因为它需要不变.

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

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

路由名称不能进行硬编码,因为所有派生类都需要具有不同的路由名称.

这是基本控制器

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是派生控制器

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

    }
}  
Run Code Online (Sandbox Code Playgroud)

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

Cod*_*ler 11

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

至于原始问题,似乎最容易解决这个问题的方法是使用CreatedAtAction而不是CreatedAtRoute.CreatedAtAction您不需要为路由命名,只需使用Get基本控制器中的操作名称即可.如果CreatedAtAction从中调用DerivedControllerA,它将生成用于Get操作的URL DerivedControllerA,如果调用DerivedControllerB它,则将生成用于Get操作的URL 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);
}
Run Code Online (Sandbox Code Playgroud)

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

    return CreatedAtAction(nameof(Get), new
    {
        //  Put actual id here
        id = 123
    }, null);
Run Code Online (Sandbox Code Playgroud)

如果出于某种原因你想坚持使用CreatedAtRoutecall,那么我想到的唯一可能的解决方案就是在每个派生类中都有不同的动作,它只是用实际逻辑调用base方法:

[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
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这种解决方案BaseController实际上会降低使用率.