ASP.NET Core Web API のコントローラー アクションの戻り値の型
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
ASP.NET Coreには、Web API コントローラー アクションの戻り値の種類に対して次のオプションが用意されています。
この記事では、各戻り値の型を使用するのが最も適切なタイミングについて説明します。
特定の型
最も基本的なアクションは、プリミティブまたは複合データ型 (たとえば、 string
またはカスタム オブジェクト) を返します。 カスタム Product
オブジェクトのコレクションを返す次のアクションを考えてみましょう。
[HttpGet]
public Task<List<Product>> Get() =>
_productContext.Products.OrderBy(p => p.Name).ToListAsync();
保護する既知の条件がなければ、特定の型を返しても十分です。 前のアクションでパラメーターを受け取っていないので、パラメーターの制約の検証は必要ありません。
複数の戻り値の型がある場合、ActionResult の戻り値の型とプリミティブまたは複合の戻り値の型を混在させるのが一般的です。 この種類のアクションに対応するには、 IActionResult または ActionResult<T> が必要です。 この記事では、複数の戻り値の型のサンプルをいくつか示します。
IEnumerable<T> または IAsyncEnumerable<T を返す>
パフォーマンスに関する考慮事項については、「 戻る IEnumerable<T>
」または IAsyncEnumerable<T>
「」を参照してください。
ASP.NET Coreは、応答に書き込む前に返IEnumerable<T>されるアクションの結果をバッファーに格納します。 非同期イテレーションを保証するために、アクション シグネチャの戻り値の型を として IAsyncEnumerable<T> 宣言することを検討してください。 最終的には、イテレーション モードは返される基になる具象型に基づいており、選択したフォーマッタは結果の処理方法に影響します。
- フォーマッタを使用
System.Text.Json
する場合、MVC は、結果をストリーミングするために追加されたSystem.Text.Json
サポートに依存します。 - フォーマッタで または を
XML-based
使用Newtonsoft.Json
すると、結果はバッファーに格納されます。
販売価格が付けられた製品レコードが IEnumerable<Product>
として返される次のようなアクションを考えてみます。
[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
var products = _productContext.Products.OrderBy(p => p.Name).ToList();
foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
前の IAsyncEnumerable<Product>
のアクションに相当するものは次のとおりです。
[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
var products = _productContext.Products.OrderBy(p => p.Name).AsAsyncEnumerable();
await foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
IActionResult 型
戻り値の型 IActionResult は、アクションの戻り値の型 ActionResult
が複数考えられる場合に適しています。 ActionResult
型は、さまざまな HTTP 状態コードを表します。 ActionResult
から派生したすべての非抽象クラスは、有効な戻り値の型として修飾されます。 このカテゴリの一般的な戻り値の型として、BadRequestResult (400)、NotFoundResult (404)、OkObjectResult (200) などがあります。 また、ControllerBase クラスの便利なメソッドを使用して、アクションから ActionResult
型を返すこともできます。 たとえば、return BadRequest();
は、return new BadRequestResult();
の短縮形です。
この種類のアクションには複数の戻り値の型とパスがあるため、[ProducesResponseType]
属性を十分に使える必要があります。 この属性は、Swagger などのツールで生成される Web API ヘルプ ページのよりわかりやすい応答の詳細を生成します。 [ProducesResponseType]
は、アクションによって返される既知の型と HTTP 状態コードを示します。
同期アクション
2 つの戻り値の型が考えられる、次の同期アクションを考えてみましょう。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById_IActionResult(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? NotFound() : Ok(product);
}
前のアクションの内容:
id
によって表される製品が、基になるデータ ストアに存在しないと、404 状態コードが返されます。 NotFound の便利なメソッドは、return new NotFoundResult();
の短縮形として呼び出されます。- 製品が存在する場合は、
Product
オブジェクトと共に 200 状態コードが返されます。 Ok の便利なメソッドは、return new OkObjectResult(product);
の短縮形として呼び出されます。
非同期アクション
2 つの戻り値の型が考えられる、次の非同期アクションを考えてみましょう。
[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync_IActionResult(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
return CreatedAtAction(nameof(GetById_IActionResult), new { id = product.Id }, product);
}
前のアクションの内容:
製品の説明に "XYZ Widget" が含まれている場合は、400 状態コードが返されます。 BadRequest の便利なメソッドは、
return new BadRequestResult();
の短縮形として呼び出されます。201 状態コードは、製品の作成時に CreatedAtAction の便利なメソッドによって生成されます。 次のコードは、 を呼び出す代わりに使用
CreatedAtAction
します。return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);
前のコード パスでは、
Product
オブジェクトが応答本文で提供されています。 新しく作成された製品の URL を含むLocation
応答ヘッダーが用意されています。
たとえば、次のモデルでは、要求に Name
プロパティと Description
プロパティを含める必要があることを示しています。 要求で Name
と Description
を提供するのに失敗すると、モデルの検証が失敗します。
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; } = string.Empty;
[Required]
public string Description { get; set; } = string.Empty;
public bool IsOnSale { get; set; }
}
属性が [ApiController]
適用されている場合、モデル検証エラーの結果、状態コードが 400 になります。 詳細については、「自動的な HTTP 400 応答」を参照してください。
ActionResult と IActionResult
次のセクションでは、ActionResult
IActionResult
ActionResult<T> 型
ASP.NET Coreには、Web API コントローラー アクションの ActionResult<T> 戻り値の型が含まれています。 これにより、 から ActionResult 派生した型を返すか、特定の 型を返します。 ActionResult<T>
により、IActionResult 型は次の利点を得られます。
[ProducesResponseType]
属性のType
プロパティを除外することができます。 たとえば、[ProducesResponseType(200, Type = typeof(Product))]
は[ProducesResponseType(200)]
に簡略化されます。 アクションの予期される戻り値の型は、 のActionResult<T>
からT
推論されます。- 暗黙的なキャスト演算子は、
T
とActionResult
の両方のActionResult<T>
への変換をサポートしています。T
は ObjectResult に変換されます。つまり、return new ObjectResult(T);
はreturn T;
に簡略化されます。
C# はインターフェイス上での暗黙的なキャスト演算子をサポートしていません。 そのため、ActionResult<T>
を使用するには、インターフェイスを具象型に変換する必要があります。 たとえば、次の例における IEnumerable
の使用は機能しません。
[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
_repository.GetProducts();
上記のコードを修正するための選択肢の 1 つは、_repository.GetProducts().ToList();
を返すことです。
ほとんどのアクションには、特定の戻り値の型があります。 アクションの実行中に予期しない状態が発生する場合、特定の型は返されません。 たとえば、アクションの入力パラメーターがモデルの検証に失敗する場合があります。 このような場合、通常は特定の型ではなく、適切な ActionResult
型が返されます。
同期アクション
2 つの戻り値の型が考えられる、同期アクションを考えてみましょう。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById_ActionResultOfT(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? NotFound() : product;
}
前のアクションの内容:
- 製品がデータベース内に存在しない場合、404 状態コードが返されます。
- 製品が存在する場合、対応する
Product
オブジェクトと共に 200 状態コードが返されます。
非同期アクション
2 つの戻り値の型が考えられる、非同期アクションを考えてみましょう。
[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync_ActionResultOfT(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
return CreatedAtAction(nameof(GetById_ActionResultOfT), new { id = product.Id }, product);
}
前のアクションの内容:
- 次の場合、ASP.NET Core ランタイムによって 400 状態コード (BadRequest) が返されます。
[ApiController]
属性が適用されていて、モデルの検証が失敗する。- 製品の説明に "XYZ Widget" が含まれている。
- 201 状態コードは、製品の作成時に CreatedAtAction メソッドによって生成されます。 このコード パスでは、
Product
オブジェクトは応答本文で指定されます。 新しく作成された製品の URL を含むLocation
応答ヘッダーが用意されています。
HttpResults 型
MVC 固有の組み込み結果の型 (IActionResult と ActionResult<T>) に加えて、ASP.NET Coreには、最小限の API と Web API の両方で使用できる HttpResults 型が含まれています。
MVC 固有の結果の型とは異なり、 HttpResults
:
IResult.ExecuteAsync の呼び出しによって処理される結果の実装です。
構成されたフォーマッタは利用しません。 構成されたフォーマッタを利用しないということは、次のことを意味します。
- のような
Content negotiation
一部の機能は使用できません。 - 生成される
Content-Type
は、実装によってHttpResults
決定されます。
- のような
は、最小限の HttpResults
API と Web API の間でコードを共有する場合に便利です。
IResult 型
Microsoft.AspNetCore.Http.HttpResults名前空間には、 インターフェイスを実装するクラスがIResult含まれています。 IResult
インターフェイスでは、HTTP エンドポイントの結果を表すコントラクトが定義されています。 異なる型の応答を表すさまざまな IResult
オブジェクトを作成するには、静的な Results クラスを使います。
組み込みの結果テーブルには、一般的な結果ヘルパーが示されています。
次のコードがあるとします。
[HttpGet("{id}")]
[ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IResult GetById(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? Results.NotFound() : Results.Ok(product);
}
前のアクションの内容:
- 製品がデータベース内に存在しない場合、404 状態コードが返されます。
- 製品が存在する場合、Results.Ok<T>() によって生成された 200 状態コードが、対応する
Product
オブジェクトと共に返されます。
次のコードがあるとします。
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(Product), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IResult> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return Results.BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
var location = Url.Action(nameof(GetById), new { id = product.Id }) ?? $"/{product.Id}";
return Results.Created(location, product);
}
前のアクションの内容:
- 次の場合、400 状態コードが返されます。
[ApiController]
属性が適用されていて、モデルの検証が失敗する。- 製品の説明に "XYZ Widget" が含まれている。
- 201 状態コードは、製品の作成時に
Results.Create
メソッドによって生成されます。 このコード パスでは、Product
オブジェクトは応答本文で指定されます。 新しく作成された製品の URL を含むLocation
応答ヘッダーが用意されています。
結果<T> 型
静的 TypedResults クラスは、戻り値の型として を使用できる具象 IResult
実装を IResult
返します。 具体的な IResult
実装を使用すると、 IResult 型に対して次の利点があります。
- 実装は
[ProducesResponseType]
エンドポイントメタデータに自動的にHttpResult
貢献するため、すべての属性を除外できます。
複数の IResult
戻り値の型が必要な場合は、 を返す Result<TResult1, TResultN>
よりも を返 IResult
す方が推奨されます。 Result<TResult1, TResultN>
ジェネリック共用体の型ではエンドポイント メタデータが自動的に保持されるため、戻り値を返す方が推奨されます。
共用体型は Results<TResult1, TResultN>
暗黙的なキャスト演算子を実装するため、コンパイラはジェネリック引数で指定された型を共用体型のインスタンスに自動的に変換できます。 これにはさらに、ルート ハンドラーから宣言されている結果のみが実際に返されることがコンパイル時にチェックされるという利点があります。 ジェネリック引数の 1 つとして宣言されていない型を Results<>
に返そうとすると、コンパイル エラーが発生します。
次のコードがあるとします。
[HttpGet("{id}")]
public Results<NotFound, Ok<Product>> GetById(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? TypedResults.NotFound() : TypedResults.Ok(product);
}
前のアクションの内容:
- 製品がデータベース内に存在しない場合、404 状態コードが返されます。
- TypedResults.Ok<T> によって生成された製品が存在する場合は、対応する
Product
オブジェクトと共に 200 状態コードが返されます。
[HttpPost]
public async Task<Results<BadRequest, Created<Product>>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return TypedResults.BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
var location = Url.Action(nameof(GetById), new { id = product.Id }) ?? $"/{product.Id}";
return TypedResults.Created(location, product);
}
前のアクションの内容:
- 次の場合、400 状態コードが返されます。
- 属性が
[ApiController]
適用され、モデルの検証が失敗します。 - 製品の説明に "XYZ Widget" が含まれている。
- 属性が
- 201 状態コードは、製品の作成時に
TypedResults.Create
メソッドによって生成されます。 このコード パスでは、Product
オブジェクトは応答本文で指定されます。 新しく作成された製品の URL を含むLocation
応答ヘッダーが用意されています。
その他の技術情報
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
ASP.NET Core では、Web API コントローラー アクションの戻り値の型に次のオプションを提供しています。
このドキュメントでは、各戻り値の型を使用するのが最適な場合について説明します。
特定の型
最もシンプルなアクションでは、プリミティブ データ型または複合データ型が返されます (string
やカスタム オブジェクトの型など)。 カスタム Product
オブジェクトのコレクションを返す次のアクションを考えてみましょう。
[HttpGet]
public List<Product> Get() =>
_repository.GetProducts();
アクションの実行中に保護できる既知の条件がない場合は、特定の型を返すだけで十分です。 前のアクションでパラメーターを受け取っていないので、パラメーターの制約の検証は必要ありません。
複数の戻り値の型がある場合、ActionResult の戻り値の型とプリミティブまたは複合の戻り値の型を混在させるのが一般的です。 この種類のアクションに対応するには、 IActionResult または ActionResult<T> が必要です。 このドキュメントでは、複数の戻り値の型のサンプルをいくつか示します。
IEnumerable<T> または IAsyncEnumerable<T を返します>
ASP.NET Coreは、応答に書き込む前に返IEnumerable<T>されるアクションの結果をバッファーに格納します。 非同期反復を保証するために、アクションシグネチャの戻り値の型を として IAsyncEnumerable<T> 宣言することを検討してください。 最終的に、イテレーション モードは、返される基となる具象型に基づいています。 MVC では、IAsyncEnumerable<T>
を実装するすべての具象型が自動的にバッファーされます。
販売価格が付けられた製品レコードが IEnumerable<Product>
として返される次のようなアクションを考えてみます。
[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
var products = _repository.GetProducts();
foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
前の IAsyncEnumerable<Product>
のアクションに相当するものは次のとおりです。
[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
var products = _repository.GetProductsAsync();
await foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
IActionResult 型
戻り値の型 IActionResult は、アクションの戻り値の型 ActionResult
が複数考えられる場合に適しています。 ActionResult
型は、さまざまな HTTP 状態コードを表します。 ActionResult
から派生したすべての非抽象クラスは、有効な戻り値の型として修飾されます。 このカテゴリの一般的な戻り値の型として、BadRequestResult (400)、NotFoundResult (404)、OkObjectResult (200) などがあります。 また、ControllerBase クラスの便利なメソッドを使用して、アクションから ActionResult
型を返すこともできます。 たとえば、return BadRequest();
は、return new BadRequestResult();
の短縮形です。
この種類のアクションには複数の戻り値の型とパスがあるため、[ProducesResponseType]
属性を十分に使える必要があります。 この属性は、Swagger などのツールで生成される Web API ヘルプ ページのよりわかりやすい応答の詳細を生成します。 [ProducesResponseType]
は、アクションによって返される既知の型と HTTP 状態コードを示します。
同期アクション
2 つの戻り値の型が考えられる、次の同期アクションを考えてみましょう。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return Ok(product);
}
前のアクションの内容:
id
によって表される製品が、基になるデータ ストアに存在しないと、404 状態コードが返されます。 NotFound の便利なメソッドは、return new NotFoundResult();
の短縮形として呼び出されます。- 製品が存在する場合は、
Product
オブジェクトと共に 200 状態コードが返されます。 Ok の便利なメソッドは、return new OkObjectResult(product);
の短縮形として呼び出されます。
非同期アクション
2 つの戻り値の型が考えられる、次の非同期アクションを考えてみましょう。
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
前のアクションの内容:
- 製品の説明に "XYZ Widget" が含まれている場合は、400 状態コードが返されます。 BadRequest の便利なメソッドは、
return new BadRequestResult();
の短縮形として呼び出されます。 - 201 状態コードは、製品の作成時に CreatedAtAction の便利なメソッドによって生成されます。
CreatedAtAction
を呼び出す代替の方法としてreturn new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);
があります。 このコード パスでは、Product
オブジェクトは応答本文で指定されます。 新しく作成された製品の URL を含むLocation
応答ヘッダーが用意されています。
たとえば、次のモデルでは、要求に Name
プロパティと Description
プロパティを含める必要があることを示しています。 要求で Name
と Description
を提供するのに失敗すると、モデルの検証が失敗します。
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public bool IsOnSale { get; set; }
}
属性が [ApiController]
適用されている場合、モデル検証エラーの結果、状態コードは 400 になります。 詳細については、「自動的な HTTP 400 応答」を参照してください。
ActionResult と IActionResult
次のセクションでは、ActionResult
IActionResult
ActionResult<T> 型
ASP.NET Coreには、Web API コントローラー アクションの ActionResult<T> 戻り値の型が含まれています。 これで、ActionResult から派生した型を返したり、特定の型を返したりすることができるようになりました。 ActionResult<T>
により、IActionResult 型は次の利点を得られます。
[ProducesResponseType]
属性のType
プロパティを除外することができます。 たとえば、[ProducesResponseType(200, Type = typeof(Product))]
は[ProducesResponseType(200)]
に簡略化されます。 アクションの予期される戻り値の型は、代わりにActionResult<T>
のT
から推論されます。- 暗黙的なキャスト演算子は、
T
とActionResult
の両方のActionResult<T>
への変換をサポートしています。T
は ObjectResult に変換されます。つまり、return new ObjectResult(T);
はreturn T;
に簡略化されます。
C# はインターフェイス上での暗黙的なキャスト演算子をサポートしていません。 そのため、ActionResult<T>
を使用するには、インターフェイスを具象型に変換する必要があります。 たとえば、次の例における IEnumerable
の使用は機能しません。
[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
_repository.GetProducts();
上記のコードを修正するための選択肢の 1 つは、_repository.GetProducts().ToList();
を返すことです。
ほとんどのアクションには、特定の戻り値の型があります。 アクションの実行中に予期しない状態が発生する場合、特定の型は返されません。 たとえば、アクションの入力パラメーターがモデルの検証に失敗する場合があります。 このような場合、通常は特定の型ではなく、適切な ActionResult
型が返されます。
同期アクション
2 つの戻り値の型が考えられる、同期アクションを考えてみましょう。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return product;
}
前のアクションの内容:
- 製品がデータベース内に存在しない場合、404 状態コードが返されます。
- 製品が存在する場合、対応する
Product
オブジェクトと共に 200 状態コードが返されます。
非同期アクション
2 つの戻り値の型が考えられる、非同期アクションを考えてみましょう。
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
前のアクションの内容:
- 次の場合、ASP.NET Core ランタイムによって 400 状態コード (BadRequest) が返されます。
[ApiController]
属性が適用されていて、モデルの検証が失敗する。- 製品の説明に "XYZ Widget" が含まれている。
- 201 状態コードは、製品の作成時に CreatedAtAction メソッドによって生成されます。 このコード パスでは、
Product
オブジェクトは応答本文で指定されます。 新しく作成された製品の URL を含むLocation
応答ヘッダーが用意されています。