WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

Web API と要求ヘッダの application/xml

by WebSurfer 7. June 2020 14:12

.NET Framework 版の ASP.NET Web API を要求する際、要求ヘッダの Accept に application/xml が含まれると応答は JSON ではなく XML になります。

ASP.NET Web API の応答

知ってました? 実は自分は知らなかったです。(汗) ちなみに、MVC の Json メソッド、および Core 3.1 Web API と MVC の Json メソッドは、要求ヘッダの Accept に application/xml が含まれていても JSON が返ってきます。

Web API の応答をチェックするには Postman などのツールを使うのが一般的なようです。でも、GET 要求ならブラウザのアドレスバーに直接 URL を入力して応答を見ることでも十分だったので自分は IE11 を使ってそうしてました。それで期待した通り JSON 文字列が返ってきてました。

ところが、Chrome, Edge, Firefox では応答が JSON ではなく XML になってしまいます。理由は要求ヘッダの Accept に application/xml が含まれるからのようです。ハマったのは上の画像のようなサーバーエラーとなってしまったことです(HTTP 500 応答が返ってきます。上の画像は形式は XML ですが中身はエラーメッセージです)。

エラーメッセージ "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'." によるとシリアライズに失敗したということです。

IE11 や jQuery ajax を使った検証ではシリアライズには問題はないことは確認しており、ブラウザに Chrome などを使ったからと言ってそれがシリアライズの部分に影響があるとは考えられなかったです。何故なのでしょう?

循環参照の問題でした。

上の画像の InnerException のエラーメッセージにある Blog_E36679EB... D5AD0 という文字列が、先の記事「JSON シリアライズの際の循環参照エラー」にもあったのを思い出して気が付きました。

.NET Framework 版の ASP.NET Web API の JSON シリアライザは Newtonsoft の Json.NET のもので、JsonIgnoreAttribute クラスという属性が利用できます。なので、問題のプロパティに [JsonIgnore] を付与してシリアライズの際の循環参照エラーは回避していました。

でも [JsonIgnore] が有効なのは JSON にシリアライズするときのみで、XML にシリアライズするときは循環参照の問題は回避できません。

Chrome, Edge などを使った時は、ASP.NET Web API は要求ヘッダの Accept に application/xml が含まれるのを見て、XML にシリアライズしようとして循環参照の問題に陥ったということのようです。

Tags: , , ,

Web API

XML ファイルを DataGridView に表示

by WebSurfer 26. April 2019 12:14

XML ファイルからデータを取得して Windows Forms アプリの DataGridView に表示し、それをユーザーが編集して結果を XML ファイルに書き戻すサンプルを書きます。

XML ファイルを DataGridView に表示

ASP.NET Web Forms アプリの場合は先の記事「XML ファイルの更新操作」に書きましたのでそちらを見てください。

アプリの基本的な構成は、DataGridView ⇔ BindingSource ⇔ DataSet ⇔ XML ファイルとしています。

DataSet.ReadXml メソッドで XML ファイルからデータを DataSet に読み込み、BindingSource 経由で DataGridView に表示。ユーザーが DataGridView に表示されたデータを編集後(編集結果は DataSet に反映されます)、ボタンクリックで DataSet.WriteXml メソッドにより編集結果を XML ファイルに書き戻すという操作を行います。

Windows Forms アプリの場合、ASP.NET Web Froms アプリとは違って、DataSet や DataGridView などすべてのインスタンスをユーザーの PC メモリ上に保持できますので、上記の操作が可能になります。

コードは、表示・編集・更新を行うためのごく基本的な部分だけですが、以下の通りです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form6 : Form
    {
        private DataGridView dataGridView1;
        private BindingSource bindingSource1;
        private DataSet dataset1;
        private string dir;
        private string file;

        public Form6()
        {
            InitializeComponent();            
        }

        private void Form6_Load(object sender, EventArgs e)
        {
            // BindingSource をデザイナで追加すると、以下のコードは .Designer.cs
            // に定義されるが、この記事のようにした場合は自力で書かないとダメ
            this.components = new System.ComponentModel.Container();
            this.bindingSource1 = new BindingSource(this.components);

            // DataGridView と DataSet はいずれも IDisposable を継承している
            // ので.Designer.cs で定義済みの Dispose(bool) メソッドで開放する
            // ため以下のようにした ・・・が、ホントに必要か?
            // 初期化する前に components.Add しても無効のようなので注意
            this.dataGridView1 = new DataGridView();
            this.dataset1 = new DataSet();            
            this.components.Add(this.dataGridView1);
            this.components.Add(this.dataset1);

            this.dataGridView1.Dock = DockStyle.Fill;            
            this.dataGridView1.DataSource = this.bindingSource1;
            this.Controls.Add(this.dataGridView1);

            this.dir = @"XML ファイルのあるフォルダ";
            this.file = "XML ファイル";
            this.dataset1.ReadXml(dir + file);

            this.bindingSource1.DataSource = dataset1.Tables[0];
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Validate();
            this.bindingSource1.EndEdit();
            this.dataset1.WriteXml(dir + file, 
                                   XmlWriteMode.WriteSchema);
            this.dataset1.AcceptChanges();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.bindingSource1.RemoveCurrent();
        }
    }
}

XML ファイルはスキーマ付きにしています。その結果、DataTable の各列の DataType はスキーマに応じて型が設定され、例えば bool 型の場合はチェックボックスが表示されます。

DataSet.WriteXml で DELETE 操作を行うためには DataTable の当該行の DataRow.RowState プロパティを DataRowSate.Deleted に設定する必要があります。それを BindingSource.RemoveCurrent メソッドで行っています。

Tags: , , , ,

.NET Framework

jQuery Ajax で xml 形式のデータ受け取り

by WebSurfer 17. July 2012 21:37

jQuery Ajax で Web サービスを呼び出し、xml 形式のデータを受け取る場合の話です。

まず、Web サービスのメソッドの戻り値の型の形式として XML を指定するときは、ScriptMethodAttribute 属性 を追加し、ResponseFormat プロパティ を Xml に設定します。さらに、メソッドが XmlDocument オブジェクト を返すようにします。以下のような感じです。

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlDocument GetXmlDocument()
{
    // Xml 形式の文字列を組み立て。
    string _xmlString = CreateXmlString();

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(_xmlString);
    return xmlDoc;
}

こうしておくと、XmlDocument オブジェクトはサーバーで Xml 形式の文字列にシリアル化され、このメソッドを呼んだクライアントに返されます。また、応答ヘッダには Content-Type: text/xml が含まれるようになります。

ScriptMethod 属性の ResponseFormat プロパティを Xml に設定しておかない場合、Web サービスのメソッドが返すデータの形式は、クライアントからの要求ヘッダの Content-Type の指定によるようです。

クライアントからサーバーへデータを送信する場合、要求ヘッダの Content-Type は、application/x-www-form-urlencoded または application/json のいずれかになるはずです(データを送信しない場合、Content-Type を指定しないこともありますが)。

前者は普通に form データをサーバーに POST する時のデータ形式、後者は Ajax でのデータ交換フォーマットのデファクトスタンダード(?)である JSON 形式です。

要求ヘッダの Content-Type が application/json となっている場合、ScriptMethod 属性の ResponseFormat プロパティを Xml に設定しておかないと、戻り値が Xml 形式にならない(といって、正しい Json 形式にもならない)ので注意してください。

要求ヘッダの Content-Type を設定しない、または application/x-www-form-urlencoded とした場合は、ResponseFormat プロパティの設定に関係なく、戻り値は Xml 形式になります。(ResponseFormat プロパティを Json 設定しても無視されて、戻り値は Xml 形式になります。)

クライアントで Web サービスからのデータを受け取るのは jQuery Ajax の success ハンドラです。jQuery Ajax は、サーバーからの応答ヘッダに含まれる Contetnt-Type を見て success ハンドラに渡すデータの形式を決めます。

jQuery API の解説ページ jQuery.ajax() に次の説明があります。

"The text and xml types return the data with no processing. The data is simply passed on to the success handler, either through the responseText or responseXML property of the jqXHR object, respectively."

上のサンプルに書いたコードの場合、Web サービスからの応答ヘッダに Contente-Type: text/xml が含まれますので、success ハンドラに渡される data は jqXHR.responseXML から取得されます。

jqXHR.responseXML は何かというと、MSDN ライブラリの responseXML property に書いてあるように、XML DOM オブジェクト(ただのシリアル化された文字列ではなく)になります。

シリアル化された文字列を表示したい場合は、IE であれば xml プロパティが使えます。詳しくは、MSDN ライブラリの IXMLDOMDocument/DOMDocument Members を参照してください。

ただし、xml プロパティは IE 専用なので、Mozilla 系のブラウザの場合は以下のように XMLSerializer を使用できます。

function getXmlDocument() {
  $.post("163-ScriptMethodAttribute.asmx/GetXmlDocument",
    function (data, textStatus, jqXHR) {
      $('#output').empty();
      $('#output').text(data.xml || 
        (new XMLSerializer()).serializeToString(data));
    }
  );
}

または、jqXHR.responseText から取得することも可能です。

Tags: , ,

AJAX

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  August 2020  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar