ASP.NET MVC Core 2.0で作ったAPIでXMLなどの形式を返したい
Created at:
ASP.NET MVC Coreはアクションの戻り値が IActionResult ではない場合、デフォルトでJSONをシリアライズしたものを返します。しかし場合によってはJSONだけでなくXMLで返したいという場合もあるかと思います。
そのようなニーズにこたえるべくフォーマッターを登録することで様々な形式でレスポンスを返すことができる仕組みがあります。ここではXMLで返す方法を説明します。
まずXML用のフォーマッターはNuGetから Microsoft.AspNetCore.Mvc.Formatters.Xml パッケージをインストールできるのでインストールします。
次にConfigureServiceメソッドのASP.NET MVC Coreの設定でXMLのフォーマッターを登録します。
|
この状態で Accept ヘッダーフィールドに application/xml を指定してリクエストするとXMLでレスポンスが返ってくるようになります。簡単ですね。
XMLシリアライザを使うときの注意
XmlDataContractSerializerOutputFormatterでシリアライズするクラスは引数なしのコンストラクタやプロパティにsetterが必要だったりするのでご注意ください。
この辺りを忘れていると406 Not Acceptableが返ってきて悩むことになります。
URLの .json/.xml のような拡張子で出し分けたい
通常は Accept によるコンテントネゴシエーションで返すフォーマットを決定すればよいのですが、URLの一部でフォーマットを指定したいというということもあります。例えば、/People/Alice.jsonとアクセスした場合はJSONで、/People/Alice.xmlであればXMLを返すといった形です。
これを行うには FormatFilter フィルター(FormatFilterAttributeクラス)をコントローラーにつけ、ルーティングに{format}を足します。例えば以下のようになります。
|
これでフォーマットが指定された場合はそれにマッチするフォーマッターが、指定されていなければデフォルトのフォーマッターが使用されます。
|
デフォルトのフォーマッターを変更する
Acceptやフォーマット名(拡張子)によってフォーマッターを決定できない場合、フォーマッターの登録順で見て行って一番最初に利用できるもの(=必ずしも一番最初ではない)がデフォルトのフォーマッターとなります。
つまり標準では JsonOutputFormatter が選択されてJSONが返されるわけですが、デフォルトをXMLにしたいという場合もあるかもしれません。そのような場合は一度 JsonOutputFormatter を抜いて、XMLのフォーマッターを登録してから再度 JsonOutputFormatter を登録します。
|
MessagePack-CSharpを使ってみる
折角なのでレスポンスでMessagePackを返すのも試してみましょう。まあWeb APIでMessagePackを受けたり返したりしたいシーンがどのぐらいあるのかは謎といえば謎なのですが。
C#界最速のシリアライザであるところのMessagePack-CSharpにはASP.NET MVC Core用のフォーマッターが用意されているので使うのは簡単です。
まずNuGetからMessagePackとMessagePack.AspNetCoreMvcFormatterをインストールします。
インストールできたらXMLの時と同様に登録します。MessagePackのMIME Typeはapplication/x-msgpackなので拡張子をマッピングを登録する場合やリクエストのAcceptにはそれを指定してください。折角なのでInputFormatterも登録しておきます。
|
あとは適当なコントローラーとデータを作ります。ここでは /People/Alice といった形でアクセスできるようなものを作ってみます。
レスポンスに使う Person クラスはMessagePackでシリアライズできるように MessagePackObject としてマークしておきます。これはMessagePack-CSharpのお約束です。
|
あとは /People/Alice.msgpack または Accept: application/x-msgpack 付きででリクエストを投げることでMessagePackで返ってくるはずです。
|
ところでMessagePackObjectとかつけるの面倒だなーMapモードでいいんだけどなーということもあるかもしれません。そんな時はContractlessStandardResolverを使用するとよいでしょう。
|
|
|
このように簡単にMessagePackで返せるようになるので使えそうなときは積極的に使っていきたいですね。
余談: MvcOptions.RespectBrowserAcceptHeader とは何か
ASP.NET MVC CoreのMvcOptionsにはフォーマッターに関連したRespectBrowserAcceptHeader プロパティという設定が存在します。この設定は何をするものかというと「ブラウザのようにAcceptに*/*といったワイルドカードのメディアタイプを乗せて来た場合、デフォルトとするかどうか」です。
例えばそれぞれの設定時には以下のようになります。
- falseの場合: Acceptに
text/html, application/xml, */*ならデフォルトのフォーマッター(JSON)、application/xmlならXMLのフォーマッター - trueの場合: Acceptに
text/html, application/xml, */*ならXMLのフォーマッター、application/xmlならXMLのフォーマッター
つまりfalseの場合には*/*やtext/*のようなものを見つけると、Acceptで指定されたものを全て無視するのと同じになります。基本的にワイルドカードを指定してくるのはブラウザぐらいなのでブラウザのAcceptに敬意を払うかどうかということです。
このプロパティはデフォルトは false となっていますので、もしブラウザのようなAcceptも受け入れてその通りに判断してほしいというときは true を設定してください。