いくつかの派生コントローラークラスを持つ汎用コントローラーがあります。しかし、定数が必要なため、HttpGetのルート名の処理方法がわかりません。
[HttpGet("{id}", Name ="should not hard coded here for derived class")]
public virtual async Task<IActionResult> Get(int id)
ルート名が必要なのは、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);
}
}
これが派生コントローラーです
[Route("api/v1/DerivedControllerA")]
public class DerivedControllerA : BaseController<TimeOff, HRContext>
{
public DerivedControllerA(IGenericRepository<TimeOff, HRContext> repository, ILogger<DerivedControllerA> logger)
: base(repository, logger)
{
}
}
どんな助けでもいただければ幸いです、ありがとう。
MVCでのベースコントローラーの使用についてNightOwl888と議論することはありません。長所と短所があり、ベースコントローラーの使用が正当化されるプロジェクトを扱ってきました。
元の質問に関しては、この問題を回避する最も簡単な方法は、CreatedAtAction
の代わりにCreatedAtRoute
を使用することです。 CreatedAtAction
では、ルートに名前を付ける必要はありません。ベースコントローラーからGet
アクション名を使用できます。 CreatedAtAction
がDerivedControllerA
から呼び出された場合、Get
アクションのURLがDerivedControllerA
に生成され、DerivedControllerB
から呼び出された場合はGet
にDerivedControllerB
アクションの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
エラーが発生します。作成されたリソースの表現を応答で返したくない場合は、3番目のパラメーターとしてnull
を渡すことができます。
return CreatedAtAction(nameof(Get), new
{
// Put actual id here
id = 123
}, null);
何らかの理由でCreatedAtRoute
呼び出しを使い続けたい場合、私の頭に浮かぶ唯一の解決策は、実際のロジックで基本メソッドを呼び出すだけの派生クラスごとに個別のアクションを実行することです。
[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
の使用を軽視します。