web-dev-qa-db-ja.com

HttpGet属性のルート名asp.netコア2の基本ジェネリックコントローラークラスの名前

いくつかの派生コントローラークラスを持つ汎用コントローラーがあります。しかし、定数が必要なため、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)
    {

    }
}  

どんな助けでもいただければ幸いです、ありがとう。

14
HExit

MVCでのベースコントローラーの使用についてNightOwl888と議論することはありません。長所と短所があり、ベースコントローラーの使用が正当化されるプロジェクトを扱ってきました。

元の質問に関しては、この問題を回避する最も簡単な方法は、CreatedAtActionの代わりにCreatedAtRouteを使用することです。 CreatedAtActionでは、ルートに名前を付ける必要はありません。ベースコントローラーからGetアクション名を使用できます。 CreatedAtActionDerivedControllerAから呼び出された場合、GetアクションのURLがDerivedControllerAに生成され、DerivedControllerBから呼び出された場合はGetDerivedControllerBアクションの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の使用を軽視します。

11
CodeFuller