ASP.NET Core Web API の応答データの書式設定

ASP.NET Core MVC では、指定した形式を使用するか、クライアントの要求に応じて、応答データを書式設定することがサポートされています。

書式固有アクションの結果

アクションの結果には、JsonResultContentResult のように、特定の形式に固有となる型があります。 アクションでは、指定された形式を常に使用して結果を返すようにし、クライアントから別の形式が要求されても無視することができます。 たとえば、JsonResult を返すときには JSON 形式のデータが返され、ContentResult を返すときにはプレーンテキスト形式の文字列データが返されます。

アクションが特定の型を返す必要はありません。 ASP.NET Core によって、すべてのオブジェクトの戻り値がサポートされます。 型が IActionResult ではないオブジェクトを返すアクションからの結果は、適切な IOutputFormatter 実装を利用してシリアル化されます。 詳細については、「ASP.NET Core Web API のコントローラー アクションの戻り値の型」を参照してください。

既定では、組み込みヘルパー メソッド ControllerBase.Ok では、JSON 形式のデータが返されます。

[HttpGet]
public IActionResult Get() =>
    Ok(_todoItemStore.GetList());

このサンプル コードでは、ToDo 項目の一覧が返されます。 F12 ブラウザー開発者ツールまたは http-repl で前のコードを使うと、次のように表示されます。

  • content-type: application/json; charset=utf-8 を含む応答ヘッダー。
  • 要求ヘッダー。 たとえば、Accept ヘッダーです。 Accept ヘッダーは前のコードでは無視されます。

プレーンテキストで書式設定されたデータを返すには、ContentResultContent ヘルパーを使用します。

[HttpGet("Version")]
public ContentResult GetVersion() =>
    Content("v1.0.0");

前のコードで、返される Content-Typetext/plain です。

戻り値の型が複数あるアクションの場合は、IActionResult を返します。 たとえば、操作の結果に基づいて異なる HTTP 状態コードを返すときです。

コンテンツ ネゴシエーション

クライアントにより Accept ヘッダーが指定されると、コンテンツ ネゴシエーションが発生します。 ASP.NET Core で使用される既定の形式は JSON です。 コンテンツ ネゴシエーションの説明を以下に示します。

  • ObjectResult によって実装されます。
  • ヘルパー メソッドから返されるステータス コード固有のアクションの結果に組み込まれます。 アクションの結果のヘルパー メソッドは ObjectResult に基づいています。

モデルの種類が返されると、戻り値の型は ObjectResult になります。

次のアクション メソッドでは、ヘルパー メソッドの OkNotFound が使用されます。

[HttpGet("{id:long}")]
public IActionResult GetById(long id)
{
    var todo = _todoItemStore.GetById(id);

    if (todo is null)
    {
        return NotFound();
    }

    return Ok(todo);
}

既定では、ASP.NET Core では次のメディアの種類がサポートされています。

  • application/json
  • text/json
  • text/plain

Fiddler や curl のようなツールでは、Accept 要求ヘッダーを設定して戻り値の形式を指定できます。 サーバーでサポートされている型が Accept ヘッダーに含まれている場合は、その型が返されます。 フォーマッタの追加方法は次のセクションで説明します。

コントローラー アクションは POCO (単純な従来の CLR オブジェクト) を返すことができます。 POCO が返されると、ランタイムはそのオブジェクトをラップする ObjectResult を自動的に作成します。 クライアントは、書式設定されたシリアル化オブジェクトを取得します。 返されるオブジェクトが null の場合は、204 No Content という応答が返されます。

次の例ではオブジェクトの種類が返されます。

[HttpGet("{id:long}")]
public TodoItem? GetById(long id) =>
    _todoItemStore.GetById(id);

前のコードでは、有効な ToDo 項目を要求すると、200 OK という応答が返されます。 無効な ToDo 項目を要求すると、204 No Content という応答が返されます。

Accept ヘッダー

コンテンツ "ネゴシエーション" は、Accept ヘッダーが要求に含まれるときに発生します。 要求に Accept ヘッダーが含まれるとき、ASP.NET Core は次のように処理します。

  • Accept ヘッダーのメディアの種類を優先順で列挙します。
  • 指定された形式のいずれかで応答を生成できるフォーマッタを見つけようとします。

クライアントの要求を満たすことができるフォーマッタが見つからない場合は、ASP.NET Core は次のようにします。

  • MvcOptions.ReturnHttpNotAcceptabletrue に設定されている場合は、406 Not Acceptable を返します。
  • 応答を生成できる最初のフォーマッタを見つけようとします。

要求された形式に対応するフォーマッタが構成されていない場合、オブジェクトを書式設定できる最初のフォーマッタが使用されます。 要求に Accept ヘッダーが表示されない場合は、次のようになります。

  • オブジェクトを処理できる最初のフォーマッタが使用されて、応答をシリアル化します。
  • ネゴシエーションは発生しません。 サーバーが返す形式を決定します。

Accept ヘッダーに */* が含まれる場合、RespectBrowserAcceptHeaderMvcOptions で true に設定されていない限り、ヘッダーが無視されます。

ブラウザーとコンテンツ ネゴシエーション

一般的な API クライアントとは異なり、Web ブラウザーは Accept ヘッダーを提供します。 Web ブラウザーでは、ワイルドカードを含め、多くの形式が指定されます。 既定では、要求がブラウザーから送信されていることをフレームワークが検出すると、次のようになります。

  • Accept ヘッダーは無視されます。
  • 特に構成されていない限り、コンテンツは JSON で返されます。

このアプローチにより、API を使用するときにブラウザー間でさらに一貫性の高いエクスペリエンスが提供されます。

ブラウザーの Accept ヘッダーを順守するようにアプリを構成するには、RespectBrowserAcceptHeader プロパティを true に設定します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.RespectBrowserAcceptHeader = true;
});

フォーマッタの構成

追加の形式をサポートする必要があるアプリでは、適切な NuGet パッケージを追加し、サポートを構成できます。 入力と出力で別々のフォーマッタがあります。 入力フォーマッタは、モデル バインドによって使用されます。 出力フォーマッタは、応答の書式設定に使用されます。 カスタム フォーマッタの作成の詳細については、カスタム フォーマッタに関するページを参照してください。

XML 形式のサポートを追加する

XmlSerializer を使用して実装された XML フォーマッタを構成するには、AddXmlSerializerFormatters を呼び出します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddXmlSerializerFormatters();

前のコードを使用する場合、コントローラー メソッドは要求の Accept ヘッダーに基づいて適切な形式を返します。

System.Text.Json ベースのフォーマッタを構成する

System.Text.Json ベースのフォーマッタの機能を構成するには、Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions を使用します。 次の強調表示されたコードでは、既定のキャメルケース書式設定の代わりに、パスカルケース書式設定を構成します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = null;
    });

次のアクション メソッドでは、ControllerBase.Problem を呼び出して ProblemDetails 応答が作成されています。

[HttpGet("Error")]
public IActionResult GetError() =>
    Problem("Something went wrong.");

ProblemDetails 応答は、アプリで書式がパスカルケースに設定されている場合でも、常にキャメルケースになります。 ProblemDetails が従う RFC 7807 では、小文字が指定されています。

特定のアクションの出力シリアル化オプションを構成するには、JsonResult を使用します。 次に例を示します。

[HttpGet]
public IActionResult Get() =>
    new JsonResult(
        _todoItemStore.GetList(),
        new JsonSerializerOptions
        {
            PropertyNamingPolicy = null
        });

Newtonsoft.Json ベースの JSON 形式のサポートを追加する

既定の JSON フォーマッタでは、System.Text.Json が使用されます。 Newtonsoft.Json ベースのフォーマッタを使用するには、Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet パッケージをインストールし、Program.cs 内でそれを構成します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddNewtonsoftJson();

前のコードでは、AddNewtonsoftJson を呼び出すことで、次の Web API、MVC、Razor Pages の各機能が、Newtonsoft.Json を使用するように構成されます。

一部の機能は System.Text.Json ベースのフォーマッタでうまく動作せず、Newtonsoft.Json ベースのフォーマッタの参照が必要となる場合があります。 アプリが以下の場合には、Newtonsoft.Json ベースのフォーマッタの使用を続けます。

  • Newtonsoft.Json 属性を使用する。 たとえば、[JsonProperty] または [JsonIgnore] です。
  • シリアル化の設定をカスタマイズする。
  • Newtonsoft.Json で提供される機能に依存する。

Newtonsoft.Json ベースのフォーマッタの機能を構成するには、SerializerSettings を使用します。

builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ContractResolver = new DefaultContractResolver();
    });

特定のアクションの出力シリアル化オプションを構成するには、JsonResult を使用します。 次に例を示します。

[HttpGet]
public IActionResult GetNewtonsoftJson() =>
    new JsonResult(
        _todoItemStore.GetList(),
        new JsonSerializerSettings
        {
            ContractResolver = new DefaultContractResolver()
        });

形式を指定する

応答の形式を制限するには、[Produces] フィルターを適用します。 ほとんどのフィルターと同様に、[Produces] をアクション、コントローラー、グローバル スコープに適用できます。

[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class TodoItemsController : ControllerBase

前の [Produces] フィルターでは以下の処理が行われます。

  • コントローラー内のすべてのアクションで、POCO (単純な従来の CLR オブジェクト) または ObjectResult とその派生型に対して JSON 形式の応答を強制的に返します。
  • 他のフォーマッタが構成されていて、クライアントが別の形式を指定している場合でも、JSON 形式の応答を返します。

詳細については、フィルターに関するページを参照してください。

特殊なケースのフォーマッタ

一部の特殊なケースが組み込みのフォーマッタで実装されます。 既定では、戻り値の型 stringtext/plain として書式設定されます (Accept ヘッダー経由で要求された場合は text/html)。 この動作は StringOutputFormatter を削除することで削除できます。 フォーマッタは Program.cs で削除されます。 戻り値の型としてモデル オブジェクトをともなうアクションは、null を返すとき、204 No Content を返します。 この動作は HttpNoContentOutputFormatter を削除することで削除できます。 次のコードでは、StringOutputFormatterHttpNoContentOutputFormatter が削除されます。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    // using Microsoft.AspNetCore.Mvc.Formatters;
    options.OutputFormatters.RemoveType<StringOutputFormatter>();
    options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
});

StringOutputFormatter がない場合は、組み込みの JSON フォーマッタによって戻り値の型 string が書式設定されます。 組み込みの JSON フォーマッタが削除され、XML フォーマッタを使用できる場合は、XML フォーマッタによって戻り値の型 string が書式設定されます。 それ以外の場合は、戻り値の型 string406 Not Acceptable が返されます。

HttpNoContentOutputFormatter がない場合、構成されているフォーマッタを利用し、null オブジェクトが書式設定されます。 次に例を示します。

  • JSON フォーマッタは、本文が null の応答を返します。
  • XML フォーマッタは、属性 xsi:nil="true" が設定された空の XML 要素を返します。

応答形式の URL マッピング

クライアントは、URL の一部として特定の形式を要求できます。次に例を示します。

  • クエリ文字列またはパスの一部。
  • .xml または .json など形式固有のファイル拡張子の使用。

要求パスからのマッピングは、API で使用されるルートに指定する必要があります。 次に例を示します。

[ApiController]
[Route("api/[controller]")]
[FormatFilter]
public class TodoItemsController : ControllerBase
{
    private readonly TodoItemStore _todoItemStore;

    public TodoItemsController(TodoItemStore todoItemStore) =>
        _todoItemStore = todoItemStore;

    [HttpGet("{id:long}.{format?}")]
    public TodoItem? GetById(long id) =>
        _todoItemStore.GetById(id);

前のルートを使用すると、要求された形式をオプションのファイル拡張子を使用して指定できます。 [FormatFilter] 属性は RouteData に形式値がないか確認し、応答が作成されると、応答形式を適切なフォーマッタにマッピングします。

ルート フォーマッタ
/api/todoitems/5 既定の出力フォーマッタ
/api/todoitems/5.json JSON フォーマッタ (構成される場合)
/api/todoitems/5.xml XML フォーマッタ (構成される場合)

ポリモーフィックな逆シリアル化

組み込み機能では、ポリモーフィックなシリアル化は限られた範囲で提供されていますが、逆シリアル化はまったくサポートされていません。 逆シリアル化を行うには、カスタム コンバーターが必要です。 ポリモーフィックな逆シリアル化の完全なサンプルについては、「ポリモーフィックな逆シリアル化」を参照してください。

その他のリソース

ASP.NET Core MVC では、応答データの書式設定がサポートされます。 応答データは、特定の形式を使用して、またはクライアントが要求した形式に応じて書式設定できます。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

書式固有アクションの結果

アクションの結果には、JsonResultContentResult のように、特定の形式に固有となる型があります。 アクションでは、クライアントの設定に関係なく、特定の形式で書式設定された結果を返すことができます。 たとえば、JsonResult を返すと、JSON 形式のデータが返されます。 ContentResult または文字列を返すと、プレーンテキスト形式の文字列データが返されます。

アクションが特定の型を返す必要はありません。 ASP.NET Core によって、すべてのオブジェクトの戻り値がサポートされます。 型が IActionResult ではないオブジェクトを返すアクションからの結果は、適切な IOutputFormatter 実装を利用してシリアル化されます。 詳細については、「ASP.NET Core Web API のコントローラー アクションの戻り値の型」を参照してください。

組み込みヘルパー メソッド Ok では、JSON 形式のデータが返されます。

// GET: api/authors
[HttpGet]
public ActionResult Get()
{
    return Ok(_authors.List());
}

サンプル ダウンロードでは作成者の一覧が返されます。 F12 ブラウザー開発者ツールまたは http-repl で前のコードを使うと、次のようになります。

  • content-type: application/json; charset=utf-8 を含む応答ヘッダーが表示されます。
  • 要求ヘッダーが表示されます。 たとえば、Accept ヘッダーです。 Accept ヘッダーは前のコードでは無視されます。

プレーンテキストで書式設定されたデータを返すには、ContentResultContent ヘルパーを使用します。

// GET api/authors/about
[HttpGet("About")]
public ContentResult About()
{
    return Content("An API listing authors of docs.asp.net.");
}

前のコードで、返される Content-Typetext/plain です。 文字列を返すと、text/plainContent-Type が送られます。

// GET api/authors/version
[HttpGet("version")]
public string Version()
{
    return "Version 1.0.0";
}

戻り値の型が複数あるアクションの場合は、IActionResult を返します。 たとえば、実行された操作の結果に基づいて、さまざまな HTTP 状態コードを返します。

コンテンツ ネゴシエーション

クライアントにより Accept ヘッダーが指定されると、コンテンツ ネゴシエーションが発生します。 ASP.NET Core で使用される既定の形式は JSON です。 コンテンツ ネゴシエーションの説明を以下に示します。

  • ObjectResult によって実装されます。
  • ヘルパー メソッドから返されるステータス コード固有のアクションの結果に組み込まれます。 アクションの結果のヘルパー メソッドは ObjectResult に基づいています。

モデルの種類が返されると、戻り値の型は ObjectResult になります。

次のアクション メソッドでは、ヘルパー メソッドの OkNotFound が使用されます。

// GET: api/authors/search?namelike=th
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
    var result = _authors.GetByNameSubstring(namelike);
    if (!result.Any())
    {
        return NotFound(namelike);
    }
    return Ok(result);
}

ASP.NET Core では、規定で application/jsontext/jsontext/plain のメディアの種類がサポートされています。 Fiddlerhttp-repl のようなツールを使うと、Accept 要求ヘッダーを設定して戻り値の形式を指定できます。 サーバーでサポートされている型が Accept ヘッダーに含まれている場合は、その型が返されます。 フォーマッタの追加方法は次のセクションで説明します。

コントローラー アクションは POCO (単純な従来の CLR オブジェクト) を返すことができます。 POCO が返されると、ランタイムはそのオブジェクトをラップする ObjectResult を自動的に作成します。 クライアントは、書式設定されたシリアル化オブジェクトを取得します。 返されるオブジェクトが null の場合は、204 No Content という応答が返されます。

オブジェクトの型を返す:

// GET api/authors/RickAndMSFT
[HttpGet("{alias}")]
public Author Get(string alias)
{
    return _authors.GetByAlias(alias);
}

前のコードでは、有効な作成者エイリアスの要求に対して、200 OK という応答と作成者のデータが返されます。 無効なエイリアスの要求に対しては、204 No Content という応答が返されます。

Accept ヘッダー

コンテンツ "ネゴシエーション" は、Accept ヘッダーが要求に含まれるときに発生します。 要求に Accept ヘッダーが含まれるとき、ASP.NET Core は次のように処理します。

  • Accept ヘッダーのメディアの種類を優先順で列挙します。
  • 指定された形式のいずれかで応答を生成できるフォーマッタを見つけようとします。

クライアントの要求を満たすことができるフォーマッタが見つからない場合は、ASP.NET Core は次のようにします。

  • MvcOptions.ReturnHttpNotAcceptabletrue に設定されている場合は、406 Not Acceptable を返します。
  • 応答を生成できる最初のフォーマッタを見つけようとします。

要求された形式に対応するフォーマッタが構成されていない場合、オブジェクトを書式設定できる最初のフォーマッタが使用されます。 要求に Accept ヘッダーが表示されない場合は、次のようになります。

  • オブジェクトを処理できる最初のフォーマッタが使用されて、応答をシリアル化します。
  • ネゴシエーションは発生しません。 サーバーが返す形式を決定します。

Accept ヘッダーに */* が含まれる場合、RespectBrowserAcceptHeaderMvcOptions で true に設定されていない限り、ヘッダーが無視されます。

ブラウザーとコンテンツ ネゴシエーション

一般的な API クライアントとは異なり、Web ブラウザーは Accept ヘッダーを提供します。 Web ブラウザーでは、ワイルドカードを含め、多くの形式が指定されます。 既定では、要求がブラウザーから送信されていることをフレームワークが検出すると、次のようになります。

  • Accept ヘッダーは無視されます。
  • 特に構成されていない限り、コンテンツは JSON で返されます。

このアプローチにより、API を使用するときにブラウザー間でさらに一貫性の高いエクスペリエンスが提供されます。

ブラウザーの Accept ヘッダーを優先するようにアプリを構成するには、RespectBrowserAcceptHeadertrue に設定します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.RespectBrowserAcceptHeader = true; // false by default
    });
}

フォーマッタの構成

追加の形式をサポートする必要があるアプリは、適切な NuGet パッケージを追加し、サポートを構成できます。 入力と出力で別々のフォーマッタがあります。 入力フォーマッタは、モデル バインドによって使用されます。 出力フォーマッタは、応答の書式設定に使用されます。 カスタム フォーマッタの作成の詳細については、カスタム フォーマッタに関するページを参照してください。

XML 形式のサポートを追加する

XmlSerializer を使用して実装された XML フォーマッタは、AddXmlSerializerFormatters を呼び出すことで構成できます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddXmlSerializerFormatters();
}

前のコードは、XmlSerializer を使用して結果をシリアル化します。

前のコードを使用する場合、コントローラー メソッドは要求の Accept ヘッダーに基づいて適切な形式を返します。

System.Text.Json ベースのフォーマッタを構成する

System.Text.Json ベースのフォーマッタの機能は、Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions を使用して構成することができます。 既定の書式設定はキャメルケースです。 次の強調されたコードは、パスカルケースで書式が設定されています。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddJsonOptions(options =>
            options.JsonSerializerOptions.PropertyNamingPolicy = null);
}

次のアクション メソッドでは、ControllerBase.Problem を呼び出して ProblemDetails 応答が作成されています。

[HttpGet("error")]
public IActionResult GetError()
{
    return Problem("Something went wrong!");
}

上のコードでは:

  • https://localhost:5001/WeatherForecast/temperature からはパスカルケースが返されます。
  • https://localhost:5001/WeatherForecast/error からはキャメルケースが返されます。 エラー応答は、アプリで書式がパスカルケースに設定されている場合でも、常にキャメルケースになります。 ProblemDetails が従う RFC 7807 では、小文字が指定されています

次のコードでは、パスカルケースが設定されて、カスタム コンバーターが追加されます。

services.AddControllers().AddJsonOptions(options =>
{
    // Use the default property (Pascal) casing.
    options.JsonSerializerOptions.PropertyNamingPolicy = null;

    // Configure a custom converter.
    options.JsonSerializerOptions.Converters.Add(new MyCustomJsonConverter());
});

出力のシリアル化オプションは、アクションごとに JsonResult を使用して構成することができます。 次に例を示します。

public IActionResult Get()
{
    return Json(model, new JsonSerializerOptions
    {
        WriteIndented = true,
    });
}

Newtonsoft.Json ベースの JSON 形式のサポートを追加する

既定の JSON フォーマッタは System.Text.Json に基づいています。 Newtonsoft.Json ベースのフォーマッタと機能のサポートは、Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet パッケージをインストールし、Startup.ConfigureServices でそれを構成することにより利用できます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddNewtonsoftJson();
}

前のコードでは、AddNewtonsoftJson を呼び出すことで、次の Web API、MVC、Razor Pages の各機能が、Newtonsoft.Json を使用するように構成されます。

一部の機能は System.Text.Json ベースのフォーマッタでうまく動作せず、Newtonsoft.Json ベースのフォーマッタの参照が必要となる場合があります。 アプリが以下の場合には、Newtonsoft.Json ベースのフォーマッタの使用を続けます。

  • Newtonsoft.Json 属性を使用する。 たとえば、[JsonProperty] または [JsonIgnore] です。
  • シリアル化の設定をカスタマイズする。
  • Newtonsoft.Json で提供される機能に依存する。

Newtonsoft.Json ベースのフォーマッタの機能は、Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.SerializerSettings を使用して構成することができます。

services.AddControllers().AddNewtonsoftJson(options =>
{
    // Use the default property (Pascal) casing
    options.SerializerSettings.ContractResolver = new DefaultContractResolver();

    // Configure a custom converter
    options.SerializerSettings.Converters.Add(new MyCustomJsonConverter());
});

出力のシリアル化オプションは、アクションごとに JsonResult を使用して構成することができます。 次に例を示します。

public IActionResult Get()
{
    return Json(model, new JsonSerializerSettings
    {
        Formatting = Formatting.Indented,
    });
}

形式を指定する

応答の形式を制限するには、[Produces] フィルターを適用します。 ほとんどのフィルターと同様に、[Produces] をアクション、コントローラー、グローバル スコープに適用できます。

[ApiController]
[Route("[controller]")]
[Produces("application/json")]
public class WeatherForecastController : ControllerBase
{

前の [Produces] フィルターでは以下の処理が行われます。

  • コントローラー内のすべてのアクションで、POCO (単純な従来の CLR オブジェクト) または ObjectResult とその派生型に対して JSON 形式の応答を強制的に返します。
  • 他のフォーマッタが構成されていて、クライアントが別の形式を指定した場合でも、JSON が返されます。

詳細については、フィルターに関するページを参照してください。

特殊なケースのフォーマッタ

一部の特殊なケースが組み込みのフォーマッタで実装されます。 既定では、戻り値の型 stringtext/plain として書式設定されます (Accept ヘッダー経由で要求された場合は text/html)。 この動作は StringOutputFormatter を削除することで削除できます。 フォーマッタは ConfigureServices メソッドで削除します。 戻り値の型としてモデル オブジェクトをともなうアクションは、null を返すとき、204 No Content を返します。 この動作は HttpNoContentOutputFormatter を削除することで削除できます。 次のコードでは、StringOutputFormatterHttpNoContentOutputFormatter が削除されます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        // requires using Microsoft.AspNetCore.Mvc.Formatters;
        options.OutputFormatters.RemoveType<StringOutputFormatter>();
        options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
    });
}

StringOutputFormatter がない場合は、組み込みの JSON フォーマッタによって戻り値の型 string が書式設定されます。 組み込みの JSON フォーマッタが削除され、XML フォーマッタを使用できる場合は、XML フォーマッタによって戻り値の型 string が書式設定されます。 それ以外の場合は、戻り値の型 string406 Not Acceptable が返されます。

HttpNoContentOutputFormatter がない場合、構成されているフォーマッタを利用し、null オブジェクトが書式設定されます。 次に例を示します。

  • JSON フォーマッタは、本文が null の応答を返します。
  • XML フォーマッタは、属性 xsi:nil="true" が設定された空の XML 要素を返します。

応答形式の URL マッピング

クライアントは、URL の一部として特定の形式を要求できます。次に例を示します。

  • クエリ文字列またはパスの一部。
  • .xml または .json など形式固有のファイル拡張子の使用。

要求パスからのマッピングは、API で使用されるルートに指定する必要があります。 次に例を示します。

[Route("api/[controller]")]
[ApiController]
[FormatFilter]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}.{format?}")]
    public Product Get(int id)
    {

前のルートを使用すると、要求された形式をオプションのファイル拡張子として指定できます。 [FormatFilter] 属性は RouteData に形式値がないか確認し、応答が作成されると、応答形式を適切なフォーマッタにマッピングします。

ルート フォーマッタ
/api/products/5 既定の出力フォーマッタ
/api/products/5.json JSON フォーマッタ (構成される場合)
/api/products/5.xml XML フォーマッタ (構成される場合)

ASP.NET Core MVC では、指定した形式を使用するか、クライアントの要求に応じて、応答データを書式設定することがサポートされています。

書式固有アクションの結果

アクションの結果には、JsonResultContentResult のように、特定の形式に固有となる型があります。 アクションでは、指定された形式を常に使用して結果を返すようにし、クライアントから別の形式が要求されても無視することができます。 たとえば、JsonResult を返すときには JSON 形式のデータが返され、ContentResult を返すときにはプレーンテキスト形式の文字列データが返されます。

アクションが特定の型を返す必要はありません。 ASP.NET Core によって、すべてのオブジェクトの戻り値がサポートされます。 型が IActionResult ではないオブジェクトを返すアクションからの結果は、適切な IOutputFormatter 実装を利用してシリアル化されます。 詳細については、「ASP.NET Core Web API のコントローラー アクションの戻り値の型」を参照してください。

既定では、組み込みヘルパー メソッド ControllerBase.Ok では、JSON 形式のデータが返されます。

[HttpGet]
public IActionResult Get()
    => Ok(_todoItemStore.GetList());

このサンプル コードでは、ToDo 項目の一覧が返されます。 F12 ブラウザー開発者ツールまたは http-repl で前のコードを使うと、次のように表示されます。

  • content-type: application/json; charset=utf-8 を含む応答ヘッダー。
  • 要求ヘッダー。 たとえば、Accept ヘッダーです。 Accept ヘッダーは前のコードでは無視されます。

プレーンテキストで書式設定されたデータを返すには、ContentResultContent ヘルパーを使用します。

[HttpGet("Version")]
public ContentResult GetVersion()
    => Content("v1.0.0");

前のコードで、返される Content-Typetext/plain です。

戻り値の型が複数あるアクションの場合は、IActionResult を返します。 たとえば、操作の結果に基づいて異なる HTTP 状態コードを返すときです。

コンテンツ ネゴシエーション

クライアントにより Accept ヘッダーが指定されると、コンテンツ ネゴシエーションが発生します。 ASP.NET Core で使用される既定の形式は JSON です。 コンテンツ ネゴシエーションの説明を以下に示します。

  • ObjectResult によって実装されます。
  • ヘルパー メソッドから返されるステータス コード固有のアクションの結果に組み込まれます。 アクションの結果のヘルパー メソッドは ObjectResult に基づいています。

モデルの種類が返されると、戻り値の型は ObjectResult になります。

次のアクション メソッドでは、ヘルパー メソッドの OkNotFound が使用されます。

[HttpGet("{id:long}")]
public IActionResult GetById(long id)
{
    var todo = _todoItemStore.GetById(id);

    if (todo is null)
    {
        return NotFound();
    }

    return Ok(todo);
}

既定では、ASP.NET Core では次のメディアの種類がサポートされています。

  • application/json
  • text/json
  • text/plain

Fiddlerhttp-repl のようなツールを使うと、Accept 要求ヘッダーを設定して戻り値の形式を指定できます。 サーバーでサポートされている型が Accept ヘッダーに含まれている場合は、その型が返されます。 フォーマッタの追加方法は次のセクションで説明します。

コントローラー アクションは POCO (単純な従来の CLR オブジェクト) を返すことができます。 POCO が返されると、ランタイムはそのオブジェクトをラップする ObjectResult を自動的に作成します。 クライアントは、書式設定されたシリアル化オブジェクトを取得します。 返されるオブジェクトが null の場合は、204 No Content という応答が返されます。

次の例ではオブジェクトの種類が返されます。

[HttpGet("{id:long}")]
public TodoItem? GetById(long id)
    => _todoItemStore.GetById(id);

前のコードでは、有効な ToDo 項目を要求すると、200 OK という応答が返されます。 無効な ToDo 項目を要求すると、204 No Content という応答が返されます。

Accept ヘッダー

コンテンツ "ネゴシエーション" は、Accept ヘッダーが要求に含まれるときに発生します。 要求に Accept ヘッダーが含まれるとき、ASP.NET Core は次のように処理します。

  • Accept ヘッダーのメディアの種類を優先順で列挙します。
  • 指定された形式のいずれかで応答を生成できるフォーマッタを見つけようとします。

クライアントの要求を満たすことができるフォーマッタが見つからない場合は、ASP.NET Core は次のようにします。

  • MvcOptions.ReturnHttpNotAcceptabletrue に設定されている場合は、406 Not Acceptable を返します。
  • 応答を生成できる最初のフォーマッタを見つけようとします。

要求された形式に対応するフォーマッタが構成されていない場合、オブジェクトを書式設定できる最初のフォーマッタが使用されます。 要求に Accept ヘッダーが表示されない場合は、次のようになります。

  • オブジェクトを処理できる最初のフォーマッタが使用されて、応答をシリアル化します。
  • ネゴシエーションは発生しません。 サーバーが返す形式を決定します。

Accept ヘッダーに */* が含まれる場合、RespectBrowserAcceptHeaderMvcOptions で true に設定されていない限り、ヘッダーが無視されます。

ブラウザーとコンテンツ ネゴシエーション

一般的な API クライアントとは異なり、Web ブラウザーは Accept ヘッダーを提供します。 Web ブラウザーでは、ワイルドカードを含め、多くの形式が指定されます。 既定では、要求がブラウザーから送信されていることをフレームワークが検出すると、次のようになります。

  • Accept ヘッダーは無視されます。
  • 特に構成されていない限り、コンテンツは JSON で返されます。

このアプローチにより、API を使用するときにブラウザー間でさらに一貫性の高いエクスペリエンスが提供されます。

ブラウザーの Accept ヘッダーを順守するようにアプリを構成するには、RespectBrowserAcceptHeader プロパティを true に設定します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.RespectBrowserAcceptHeader = true;
});

フォーマッタの構成

追加の形式をサポートする必要があるアプリでは、適切な NuGet パッケージを追加し、サポートを構成できます。 入力と出力で別々のフォーマッタがあります。 入力フォーマッタは、モデル バインドによって使用されます。 出力フォーマッタは、応答の書式設定に使用されます。 カスタム フォーマッタの作成の詳細については、カスタム フォーマッタに関するページを参照してください。

XML 形式のサポートを追加する

XmlSerializer を使用して実装された XML フォーマッタを構成するには、AddXmlSerializerFormatters を呼び出します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddXmlSerializerFormatters();

前のコードを使用する場合、コントローラー メソッドは要求の Accept ヘッダーに基づいて適切な形式を返します。

System.Text.Json ベースのフォーマッタを構成する

System.Text.Json ベースのフォーマッタの機能を構成するには、Microsoft.AspNetCore.Mvc.JsonOptions.JsonSerializerOptions を使用します。 次の強調表示されたコードでは、既定のキャメルケース書式設定の代わりに、パスカルケース書式設定を構成します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = null;
    });

特定のアクションの出力シリアル化オプションを構成するには、JsonResult を使用します。 次に例を示します。

[HttpGet]
public IActionResult Get() 
    => new JsonResult(
        _todoItemStore.GetList(),
        new JsonSerializerOptions { PropertyNamingPolicy = null });

Newtonsoft.Json ベースの JSON 形式のサポートを追加する

既定の JSON フォーマッタでは、System.Text.Json が使用されます。 Newtonsoft.Json ベースのフォーマッタを使用するには、Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet パッケージをインストールし、Program.cs 内でそれを構成します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddNewtonsoftJson();

前のコードでは、AddNewtonsoftJson を呼び出すことで、次の Web API、MVC、Razor Pages の各機能が、Newtonsoft.Json を使用するように構成されます。

一部の機能は System.Text.Json ベースのフォーマッタでうまく動作せず、Newtonsoft.Json ベースのフォーマッタの参照が必要となる場合があります。 アプリが以下の場合には、Newtonsoft.Json ベースのフォーマッタの使用を続けます。

  • Newtonsoft.Json 属性を使用する。 たとえば、[JsonProperty] または [JsonIgnore] です。
  • シリアル化の設定をカスタマイズする。
  • Newtonsoft.Json で提供される機能に依存する。

Newtonsoft.Json ベースのフォーマッタの機能を構成するには、SerializerSettings を使用します。

builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ContractResolver = new DefaultContractResolver();
    });

特定のアクションの出力シリアル化オプションを構成するには、JsonResult を使用します。 次に例を示します。

[HttpGet]
public IActionResult GetNewtonsoftJson()
    => new JsonResult(
        _todoItemStore.GetList(),
        new JsonSerializerSettings { ContractResolver = new DefaultContractResolver() });

ProblemDetails および ValidationProblemDetails 応答の書式設定

次のアクション メソッドでは、ControllerBase.Problem を呼び出して ProblemDetails 応答が作成されています。

[HttpGet("Error")]
public IActionResult GetError()
    => Problem("Something went wrong.");

ProblemDetails 応答は、アプリで書式がパスカルケースに設定されている場合でも、常にキャメルケースになります。 ProblemDetails が従う RFC 7807 では、小文字が指定されています。

[ApiController] 属性がコントローラー クラスに適用されると、モデル検証が失敗したときにコントローラーによって ValidationProblemDetails 応答が作成されます。 この応答には、モデルのプロパティ名をエラー キーとして変更せずに使用するディクショナリが含まれます。 たとえば、次のモデルには、検証を必要とする 1 つのプロパティが含まれています。

public class SampleModel
{
    [Range(1, 10)]
    public int Value { get; set; }
}

既定では、Value プロパティが無効な場合に返される ValidationProblemDetails 応答では、次の例に示すように、Value というエラー キーが使用されます。

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-00000000000000000000000000000000-000000000000000-00",
  "errors": {
    "Value": [
      "The field Value must be between 1 and 10."
    ]
  }
}

エラー キーとして使用されるプロパティ名を書式設定するには、IMetadataDetailsProvider の実装を MvcOptions.ModelMetadataDetailsProviders コレクションに追加します。 次の例では、System.Text.Json ベースの実装 SystemTextJsonValidationMetadataProvider を追加します。これにより、既定でプロパティ名がキャメルケースとして書式設定されます。

builder.Services.AddControllers();

builder.Services.Configure<MvcOptions>(options =>
{
    options.ModelMetadataDetailsProviders.Add(
        new SystemTextJsonValidationMetadataProvider());
});

SystemTextJsonValidationMetadataProvider は、コンストラクターで JsonNamingPolicy の実装も受け付けます。これにより、プロパティ名を書式設定するためのカスタム名前付けポリシーを指定します。

モデル内のプロパティにカスタム名を設定するには、プロパティの [JsonPropertyName] 属性を使用します。

public class SampleModel
{
    [Range(1, 10)]
    [JsonPropertyName("sampleValue")]
    public int Value { get; set; }
}

前のモデルで Value プロパティが無効な場合に返される ValidationProblemDetails 応答では、次の例に示すように、sampleValue というエラー キーが使用されます。

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-00000000000000000000000000000000-000000000000000-00",
  "errors": {
    "sampleValue": [
      "The field Value must be between 1 and 10."
    ]
  }
}

Newtonsoft.Json を使用して ValidationProblemDetails 応答を書式設定するには、NewtonsoftJsonValidationMetadataProvider を使用します。

builder.Services.AddControllers()
    .AddNewtonsoftJson();

builder.Services.Configure<MvcOptions>(options =>
{
    options.ModelMetadataDetailsProviders.Add(
        new NewtonsoftJsonValidationMetadataProvider());
});

既定では、NewtonsoftJsonValidationMetadataProvider によりプロパティ名はキャメルケースとして書式設定されます。 NewtonsoftJsonValidationMetadataProvider は、コンストラクターで NamingPolicy の実装も受け付けます。これにより、プロパティ名を書式設定するためのカスタム名前付けポリシーを指定します。 モデル内のプロパティにカスタム名を設定するには、[JsonProperty] 属性を使用します。

形式を指定する

応答の形式を制限するには、[Produces] フィルターを適用します。 ほとんどのフィルターと同様に、[Produces] をアクション、コントローラー、グローバル スコープに適用できます。

[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class TodoItemsController : ControllerBase

前の [Produces] フィルターでは以下の処理が行われます。

  • コントローラー内のすべてのアクションで、POCO (単純な従来の CLR オブジェクト) または ObjectResult とその派生型に対して JSON 形式の応答を強制的に返します。
  • 他のフォーマッタが構成されていて、クライアントが別の形式を指定している場合でも、JSON 形式の応答を返します。

詳細については、フィルターに関するページを参照してください。

特殊なケースのフォーマッタ

一部の特殊なケースが組み込みのフォーマッタで実装されます。 既定では、戻り値の型 stringtext/plain として書式設定されます (Accept ヘッダー経由で要求された場合は text/html)。 この動作は StringOutputFormatter を削除することで削除できます。 フォーマッタは Program.cs で削除されます。 戻り値の型としてモデル オブジェクトをともなうアクションは、null を返すとき、204 No Content を返します。 この動作は HttpNoContentOutputFormatter を削除することで削除できます。 次のコードでは、StringOutputFormatterHttpNoContentOutputFormatter が削除されます。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    // using Microsoft.AspNetCore.Mvc.Formatters;
    options.OutputFormatters.RemoveType<StringOutputFormatter>();
    options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
});

StringOutputFormatter がない場合は、組み込みの JSON フォーマッタによって戻り値の型 string が書式設定されます。 組み込みの JSON フォーマッタが削除され、XML フォーマッタを使用できる場合は、XML フォーマッタによって戻り値の型 string が書式設定されます。 それ以外の場合は、戻り値の型 string406 Not Acceptable が返されます。

HttpNoContentOutputFormatter がない場合、構成されているフォーマッタを利用し、null オブジェクトが書式設定されます。 次に例を示します。

  • JSON フォーマッタは、本文が null の応答を返します。
  • XML フォーマッタは、属性 xsi:nil="true" が設定された空の XML 要素を返します。

応答形式の URL マッピング

クライアントは、URL の一部として特定の形式を要求できます。次に例を示します。

  • クエリ文字列またはパスの一部。
  • .xml または .json など形式固有のファイル拡張子の使用。

要求パスからのマッピングは、API で使用されるルートに指定する必要があります。 次に例を示します。

[ApiController]
[Route("api/[controller]")]
[FormatFilter]
public class TodoItemsController : ControllerBase
{
    private readonly TodoItemStore _todoItemStore;

    public TodoItemsController(TodoItemStore todoItemStore)
        => _todoItemStore = todoItemStore;

    [HttpGet("{id:long}.{format?}")]
    public TodoItem? GetById(long id)
        => _todoItemStore.GetById(id);

前のルートを使用すると、要求された形式をオプションのファイル拡張子を使用して指定できます。 [FormatFilter] 属性は RouteData に形式値がないか確認し、応答が作成されると、応答形式を適切なフォーマッタにマッピングします。

ルート フォーマッタ
/api/todoitems/5 既定の出力フォーマッタ
/api/todoitems/5.json JSON フォーマッタ (構成される場合)
/api/todoitems/5.xml XML フォーマッタ (構成される場合)

ポリモーフィックな逆シリアル化

組み込み機能では、ポリモーフィックなシリアル化は限られた範囲で提供されていますが、逆シリアル化はまったくサポートされていません。 逆シリアル化を行うには、カスタム コンバーターが必要です。 ポリモーフィックな逆シリアル化の完全なサンプルについては、「ポリモーフィックな逆シリアル化」を参照してください。

その他のリソース