DOM プログラミングレッスン
第3回:インターネットサーバーアプリケーションを作成する
(株)日本ユニテック
太田 純
<この記事はDigital
Xpress 2001 Vol.3(6-7月号)に掲載されたものです>
今回は、XMLを使用するインターネットサーバーアプリケーションの作成について扱っていきたいと思います。 |
ISAPI Extension Wizard
Visual C++ 6.0 で新しく作成するプロジェクトに「ISAPI Extension Wizard」を指定すると、DLL形式で提供するインターネットアプリケーションを作成することができます。
この方法で作成したプロジェクトでXMLファイルを読み込んでクライアントへ送るコードを書くと、リスト1のようになります(注1)。
ここで、関数Default()は、引数なしでDLLが参照された場合に実行される関数です。
_bstr_t strBasePath = L"C:\\sample02\\Debug\\";
//パスを設定
void CSample02Extension::Default(CHttpServerContext* pCtxt)
{
IXMLDOMDocumentPtr pDoc;
pDoc.CreateInstance(L"MSXML.DOMDocument");
VARIANT_BOOL f;
pDoc->put_async(VARIANT_FALSE);
pDoc->load(variant_t(strBasePath + L"sample02.xml"),
&f);
if(!f)
{
*pCtxt << "Cannot load file";
return;
}
BSTR strXml;
pDoc->get_xml(&strXml);
//(A)
AddHeader(pCtxt, "Content-type = text/xml\r\n");
*pCtxt << strXml; //(B)
SysFreeString(strXml);
}
void operator <<(CHttpServerContext
&sc, wchar_t *pwsz) //(C)
{
USES_CONVERSION;
sc << W2T(pwsz);
}
|
リスト1
このコードでは、MSXMLを使用してXMLファイルを読み込み、それをそのままクライアントへ送出しています。実際はなんらかの編集を加えてからクライアントへ送ることになるでしょう。さもなければそのままXMLファイルを送ればよいでしょうから。
(B)で示した行においてUnicodeからShift_JISへの変換がなされるようにするため、operator
<< をオーバーライドするコードを追加しました。これがリスト1の(C)の部分です。
ブラウザがIE5.0など、XMLファイルの表示をサポートしているものであればこれで表示されるように思えます。しかし、実際にはこのコードで正しく表示することはできません。この場合の表示結果は図1のようになってしまいます。
図1
ブラウザからソースを表示させると、読み込み元のファイル(リスト2-1)とブラウザへ送られたデータ(リスト2-2)では微妙に変わっていることがわかります。
<?xml version="1.0" encoding="Shift_JIS"
?>
<sample02>
<question>あじょしてる?</question>
<answer>あんもだよ。</answer>
</sample02>
|
リスト2-1
<?xml version="1.0" ?>
<sample02>
<question>あじょしてる?</question>
<answer>あんもだよ。</answer>
</sample02>
|
リスト2-2
問題となるのは、1行目、XML宣言のencoding指定です。これが失われているのはどういうことなのでしょうか?
リスト1の(A)で使用しているxmlプロパティは文字列をUnicodeで返却します。ここでもし、このUnicodeの文字列に含まれているXML宣言にShift_JISを示すencoding指定が入っているとしたらつじつまが合わなくなってしまいます。したがって、xmlプロパティが返却する文字列中のXML宣言からencoding指定が消えているのは考え方として正常な動作だと言えるでしょう。
ISAPIアプリケーションをUnicodeでビルドしたらどうだろうか?と思うかもしれませんが、現状のISAPIではUnicodeはサポートしていないためこの方法を使うことはできません。
これを踏まえて修正したソースをリスト3に挙げました。
void CSample02Extension::Default(CHttpServerContext*
pCtxt)
{
IXMLDOMDocumentPtr pDoc;
pDoc.CreateInstance(L"MSXML.DOMDocument");
VARIANT_BOOL f;
pDoc->put_async(VARIANT_FALSE);
pDoc->load(variant_t(strBasePath + L"sample02u.xml"),
&f);
if(!f)
{
*pCtxt << "Cannot load file";
return;
}
BSTR strXml;
IXMLDOMElementPtr pRoot;
pDoc->get_documentElement(&pRoot);
pRoot->get_xml(&strXml);
AddHeader(pCtxt, "Content-type = text/xml\r\n");
*pCtxt << "<?xml
version=\"1.0\" encoding=\"Shift_JIS\"?>\r\n"
<< strXml;
SysFreeString(strXml);
}
|
リスト3
先頭のXML宣言が付かない形でXMLを取り出し、XML宣言は文字列で追加するようにしてみました。あまりいい感じのコードとは言えませんが、これで動作するようになります。図2に示したようにブラウザ上で表示することができました。実際にはXSLファイルを使用して整形したものを表示させるとよいでしょう。
図2
ATLを使用したサーバーコンポーネント
次に、ATL(注2)を使用したコンポーネントを作成し、それをASP(注3)から呼び出す方法を検討してみたいと思います。
ATLを使用したサーバーコンポーネントを作成するには、ATL COM AppWizardでプロジェクトを作成した後、「挿入」メニューまたはClassViewの「ATLオブジェクトの新規作成」を選択して「ActiveX
サーバーコンポーネント」を挿入します。
ここで、以下の2つの考え方のどちらかを使うことになると思います。
-
ASPには、コンポーネントを呼び出すコードのみを書き、クライアントへの応答も含めて、処理のすべてをコンポーネント側で行う。
-
表示するHTMLはASPに記述し、そこに埋め込むデータを作成するための処理をコンポーネント側に実装する。
1の方法の場合は「ActiveX サーバーコンポーネント」として作成する必要があります。この方法を取る場合は、表示部分をXSLに切り出すなどの方法と併用することをお勧めします。
2の方法の場合は「ActiveX サーバーコンポーネント」ではなく、「シンプルオブジェクト」を作成してもASPから使用することができます。
この違いは、「ActiveX サーバーコンポーネント」は、コンポーネント内からASPのオブジェクトにアクセスできるという点にあります。
(注4)言い替えると、コンポーネント内部でHTTP要求に渡された引数やフォームデータをチェックしたり、クライアントへ書き出す文字列を直接指定したりできるということです。
前述のISAPI Extensionと比較するため、ここでは1の方法を取り、読み込むXMLファイルにリスト2-1、出力として図2の表示を得るためのコードを書くとリスト4のようになります。このコードでは、読み込むXMLファイルへのパスを決定するためにリストの(D)の部分でServerオブジェクトのMapPathメソッドを使用しました。
STDMETHODIMP CXReporter::Default()
{
IXMLDOMDocumentPtr pDoc;
pDoc.CreateInstance(L"MSXML.DOMDocument");
VARIANT_BOOL f;
pDoc->put_async(VARIANT_FALSE);
CComBSTR strURL;
m_piServer->MapPath(L"sample02.xml",
&strURL); //(D)
pDoc->load(CComVariant(strURL), &f);
if(!f)
{
m_piResponse->Write(CComVariant(L"Cannot load
file"));
return S_OK;
}
m_piResponse->put_ContentType(CComBSTR(L"text/xml"));
pDoc->save(CComVariant(m_piResponse));
//(E)
return S_OK;
}
|
リスト4
このコンポーネントを呼び出すためのASPはリスト5のようになります。
<OBJECT ID="sample02obj" RunAt="Server"
CLASSID="clsid:4D5E34D8-8B16-409D-AB81-FE3CD935816E">
</OBJECT>
<%
sample02obj.Default
%>
|
リスト5
OBJECTタグのCLASSID属性の値には、ATLのWizardが生成するIDLファイルのlibraryセクション内の、使用するcoclassに付いているuuidを使用します。筆者が作成したソースの場合のlibraryセクションはリスト6のようになっています。実際にはご自分のソースの該当する部分を確認して指定してください。
[
uuid(E8A9B4D9-ADBA-4A8D-9D5B-334A77130659),
version(1.0),
helpstring("sample02ATL 1.0 タイプ ライブラリ")
]
library SAMPLE02ATLLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(4D5E34D8-8B16-409D-AB81-FE3CD935816E),
helpstring("XReporter Class")
]
coclass XReporter
{
[default] interface IXReporter;
};
};
|
リスト6
ISAPI Extensionのソースであるリスト3とリスト4の違いの一つは、リスト4の(E)で示した行にあります。
IXMLDOMDocumentのメソッドsaveは、引数にResponseオブジェクトを受け取ると、読み込んだXMLファイルのXML宣言で指定されたencodingでResponseオブジェクトへ書き出します。これによって、コンポーネント内部の文字列処理はすべてUnicodeで扱いながら、クライアントへは適切なコードでデータを送ることが可能になります。
今回は、ISAPI ExtensionおよびATLを使用したサーバーコンポーネントという2つの方法でのサーバーアプリケーションの作成方法を検討しました。今後のサーバーアプリケーションの構築には、表示部分とデータ処理部分の分離を視野に入れた上でコンポーネント化していくことをお勧めします。
(注1) エラー処理は省略してあります。実際のコーディングではHRESULT値をチェックしてください。
(注2)ATL(Active Template
Library):軽量高速なCOMオブジェクトを作成するためのC++テンプレートライブラリー。
(注3)ASP(Active Server Pages):マイクロソフトが提供するWEBベースのアプリケーション環境。ASP自体はISAPIアプリケーションとして実装されている。
(注4)ASPオブジェクト:使用できるASPオブジェクトには、Request、Response、Server、Session、Applicationの5つがあります。詳細についてはMSDNライブラリーで確認してください。
関連サービス
IT技術およびIT製品の可用性調査・検証業務
|