我有一个通用控制器,它有几个派生的控制器类。但我无法弄清楚如何处理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)
{
}
}
任何帮助将不胜感激,谢谢。
答案 0 :(得分:10)
我不会与NightOwl888讨论在MVC中使用基本控制器的问题。有利有弊,我处理过使用基本控制器的项目是合理的。
关于原始问题,似乎解决此问题的最简单方法是使用CreatedAtAction
而不是CreatedAtRoute
。 CreatedAtAction
不要求您为路由命名,您只需使用基本控制器中的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
的使用贬值。