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
而不是 CreatedAtRoute
。 CreatedAtAction
不需要你命名你的路线,你可以只使用来自基本控制器的 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
的使用。
我有一个通用控制器,它有几个派生控制器 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
而不是 CreatedAtRoute
。 CreatedAtAction
不需要你命名你的路线,你可以只使用来自基本控制器的 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
的使用。