HttpGet属性的路由名称asp.net core 2

时间:2018-02-06 01:05:06

标签: 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)

我需要路由名称,因为在我的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 个答案:

答案 0 :(得分:10)

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

关于原始问题,似乎解决此问题的最简单方法是使用CreatedAtAction而不是CreatedAtRouteCreatedAtAction不要求您为路由命名,您只需使用基本控制器中的Get操作名称即可。如果从CreatedAtAction调用DerivedControllerA,它将在Get中生成DerivedControllerA操作的网址,如果从DerivedControllerB调用,则会将在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);
}

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

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

如果由于某种原因你想坚持CreatedAtRoute调用,我想到的唯一可能的解决方案就是在每个派生类中都有不同的动作,它只是用实际逻辑调用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
    }
}

然而,这样的解决方案实际上会使BaseController的使用贬值。