ASP.NET Web API 2 に XML 形式のデータを送信し、サーバー側のアクションメソッドで受け取る方法を書きます。(注: .NET Framework 版の話です。Core 版は未検証・未確認)
ASP.NET Web API で XML データを送信するというのは需要がなさそうで、あまり役には立たないかもしれませんが、せっかく考えたことなので整理して備忘録として残しておくことにしました。
(1) UTF-8 の場合
元の話は Teratail のスレッド「ASP.NET Web APIでPOSTされたXMLデータをXML形式で取得する方法」のものです。
サンプル XML データは以下の通りです。
<?xml version="1.0" encoding="utf-8"?>
<sample code="1.0">
<data>
<id>100</id>
</data>
<value>
<name>日本語</name>
<version>1.0.0</version>
</value>
</sample>
この XML データを ASP.NET Web API に POST 送信し、サーバー側でアクションメソッド Post(SampleData value) の引数 value にモデルバインドするにはどうしたらよいかという話です。SampleData クラスの定義は以下の通り。属性を付与した理由は下に書きます。
namespace WebApi.Models
{
[System.Xml.Serialization.XmlRoot("sample")]
public class SampleData
{
[System.Xml.Serialization.XmlAttribute("code")]
public string Code { get; set; }
[System.Xml.Serialization.XmlElement("data")]
public Data Data { get; set; }
[System.Xml.Serialization.XmlElement("value")]
public Value Value { get; set; }
}
public class Data
{
[System.Xml.Serialization.XmlElement("id")]
public string Id { get; set; }
}
public class Value
{
[System.Xml.Serialization.XmlElement("name")]
public string Name { get; set; }
[System.Xml.Serialization.XmlElement("version")]
public string Version { get; set; }
}
}
XML データの文字コードが UTF-8 であれば以下の 3 つの設定でこの記事の一番上の画像の通りモデルバインドできます。
-
要求ヘッダに Content-Type: application/xml; charset=UTF-8 の設定
Microsoft のドキュメント How WebAPI does Parameter Binding には content-type の設定だけで済みそうなことが書いてありますが、自分が試した限りでは下の 2, 3 も必要でした。
-
App_Start/WebApiConfig.cs へ以下の設定の追加
config.Formatters.XmlFormatter.UseXmlSerializer = true;
-
モデルのクラス、プロパティに XmlRoot, XmlAttribute, XmlElement 属性の付与
xml コードの要素名とそれをバインドするクラス、プロパティ名を関連付けるために必要です。たとえ同名でも、上の例のように大文字小文字の違いがある場合は、プロパティにXmlElement 属性を付与してその引数に xml の要素名を設定しないとバインドされません。また、xml コードの属性値(この記事の例では code)をバインドする場合は、それに該当するプロパティへの XmlAttribute 属性の付与が必要です。
(2) Shift_JIS の場合
XML データの文字コードが Shift_JIS の場合はどうでしょう? (これも Teratail のスレッド「ASP.NET Web API POSTされた文字エンコードShift_JISのXMLデータをモデルバインドできない」の話です)
上の「(1) UTF-8 の場合」のような方法ではうまくいかないようです(null がバインドされる)。もちろん XML データを encoding="shift_jis" とし、要求ヘッダを Content-Type: application/xml; charset=shift_jis としたのですがダメでした。
検索するなどして調べてみましたが成果がなかったです。Shift_JIS ではヒットしないので UTF-16 に範囲を広げてググってみましたが、stackoverflow のスレッド Web API not able to bind model for POST with utf-16 encoded XML など、できなかったという記事しか見つかりませんでした。
そこを何とかするには、Microsoft のドキュメント How WebAPI does Parameter Binding の最初の方に書いてあるように、HttpRequestMessage クラスを引数に持つメソッドを使う他には手がなさそうな感じです。
HttpRequestMessage.Content プロパティで HttpContent オブジェクトを取得し、HttpContent.ReadAsStringAsync メソッドで POST されてきた XML 文字列を読んで、XDocument.Load メソッドで XDocument オブジェクトを作ってそれを操作するようにしてみました。
なお、この方法を取った場合、上の「UTF-8 の場合」で行った「2. App_Start/WebApiConfig.cs へ以下の設定の追加」と「3. モデルのクラス、プロパティに XmlRoot, XmlAttribute, XmlElement 属性の付与」は必要ありません。
検証は以下のようにしました。
まず Fiddler の Composer を使って、要求ヘッダに Content-Type: application/xml; charset=shift_jis を付与し、Request Body に XML データを設定して送信します。
Fiddler の Inspectors で HexView を選択し、Request Body に設定して送信した XML データの 16 進数表現をチェックします。XML データの中の <name>日本語</name> の「日本語」の文字コードは 93 FA 96 7B 8C EA と Shift_JIS になっているのが分かります。
Visual Studio のデバッガでアクションメソッドの Local 変数を確認します。「日本語」は文字化けせず期待通り取得できています。
ちょっと予想外だったのは、Fiddler の Composer の Request Body に書いた「日本語」の文字コードが Shift_JIS になることと、HttpContent.ReadAsStringAsync メソッドがそれを正しく「日本語」と読んでくれることです。そんなに都合よくできているというのが信じ難く、何か見落としがあるかも。(汗)