前回の投稿から2週間以上が過ぎすみません。
クリック証券部分について解説するために自分のプログラムKATSの該当部分も
開発当時より少し改良した方が良いと思い作業を進めるうち、Webサービス呼び出しの
クラスライブラリ自体の大幅リファクタリングとなってしまい、連載が滞ってしまいました。
ブログを読んで下さっている方から「続きを首を長くして待っております・・・
がんばってください・・・」というコメントを頂き「早く続きを書かないと!」と
思いました。今後も、もし滞っている事があれば声を掛けて頂けたらと思います。
皆さんの期待が私の活力になりますので。
さて前回はログインボタンの作成まで行いました。
いよいよ今回はログイン処理を実装していきます。
私は手元にある
「クリック証券 Webサービス仕様書【ログイン編】- 第2.1.0版 -」
を元に解説を書いていきます。
最初ログインシーケンスについての解説を読んで「ん!?」と悩まれた方も
多いのでは無いかと思います。
でも実際Webサービスを呼び出す時には、2回のリクエスト送信を意識するだけです。
では早速書いていきましょう。
まずはログインシーケンス1です。
仕様書の6ページにある要求URLと、それに付随する要求BODY(リクエストパラメータ)を
使ってHTTPリクエストを送信します。
<その3>で作成したHttpPost()メソッドを使用します。
引数はURLとパラメータですのでそのままですね。
呼び出す時にはこう書きます。
Dim res As WebResponse = HttpPost("https://~~/ws-redirect", "u=xxxxxx")
リクエストURLを伏せていますが、Confidential情報だからです。
クリック証券は口座開設した人にのみ仕様書を公開していますので。
クリック証券Webサービスを使用される方は仕様書を見ればURLは分かりますので大丈夫です。
リクエストパラメータにはユーザーIDを
「u=」の形で渡します。
もちろんxxxxxxにはご自分のクリック証券ユーザーIDを記述して下さい。
ちなみにこの1回の処理で、ログインシーケンス2の終了まで処理が行われます。
ログインシーケンス1はリダイレクト応答なので、自動的にログインシーケンス2も
行われて戻り値のWebResponseはログインシーケンス2後の応答となります。
まずここまでして、戻って来たXML情報を覗いてみる事にしましょう。
以下のソースは応答XMLが目に見えるように、仮に記述するものです。
Dim sr As New System.IO.StreamReader(res.GetResponseStream())
Console.WriteLine(sr.ReadToEnd())
記述出来たら早速実行して、ログインボタンを押してみましょう。
出力ウィンドウに以下のように表示されたでしょうか?
もし結果が失敗の場合は、クリック証券のサービスを有効にしていないか、
URLが間違っているか、ユーザーIDが間違っている可能性があるのでチェックして下さい。
人間が目で見たら「プレ認証が成功している」と分かります。
ですが、ログインシーケンスの続きを行うためには、このXML情報をプログラムが解釈して
「OK」を認識しないといけません。
色々なやり方がありますが、私が最も有効だと思っているやり方は、
DataSetを使う方法です。
「ナンじゃそれ!?」と思われた方。確かに.NETでDB処理のプログラミングをされていない方は
馴染みの無い名前だと思います。
DataSetあるいはDataTableというのは、RDBをメモリ上で表現する非接続型のデータオブジェクトです。
元々はデータベースの情報を扱う時に便利なものなのです。
DataTableは1テーブルを表し、そのレコードとしてDataRowがあったり。
DataTableの集合としてDataSetが存在します。
ここではDataSetやDataTableなどの解説は行いません。
http://msdn.microsoft.com/ja-jp/library/zb0sdh0b.aspx
http://www.atmarkit.co.jp/fdotnet/basics/adonet04/adonet04_01.html
などで概念が分かるかも知れませんが、少々ややこしいですし今回はDBアクセスに使用する訳では
無いのであまり触れないでおきます。
でもDataSetはXMLとの親和性が高く、それを利用しない手は無いと私は思っています。
DataSetにXML情報を取り込むと、各要素をプロパティで取り出す事が可能になるからです。
能書きはこれぐらいにして早速やってみます。
先ほど書いた仮の2行の代わりに以下のコードを記述します。
Dim ds As New DataSet
Using resStream As Stream = res.GetResponseStream()
ds.ReadXml(resStream)
End Using
「Usingブロック」は楽天RSS編でも解説した事があります。
確実にリソースを解放するために記述しています。詳細の解説は割愛します。
実際やっているのは、DataSetをインスタンス化してReadXml()を呼んでいるだけです。
以上で応答XMLをDataSetに読み込めましたが、データはどのように入っているのでしょうか?
応答XMLは
<?xml version="1.0" encoding="UTF-8"?>
<loginResponse>
<responseStatus>OK</responseStatus>
<message>PreAuthentication Success.</message>
<loginResponse>
でした。
1行目の情報はDataSetには入らず、実データの4行が解釈されます。
DataSetの中には
「loginResponse」というテーブル名のDataTableが1つ。
そしてDataTableの中には
「responseStatus」というカラムと
「message」というカラムを持った
データ1行が入っています。
それを踏まえて
「responseStatus」の内容
「OK」を取得するためにはどう記述したら良いか。
このようになります。
Console.WriteLine(ds.Tables("loginResponse").Rows(0).Item("responseStatus"))
DataSetの中のloginResponseテーブルの0行目(0行目から始まります)の、
responseStatusカラムの値です。
実行して出力ウィンドウを確認すると、もちろん「OK」と表示されています。
これで応答XML情報の内容を確認する事が可能になりました。
プレ認証の結果を元にログインシーケンスの後続処理を続ける事が出来ます。
プレ認証に失敗したら結果は「NG」と返って来ますので、NGの場合はメッセージボックスを
表示して終了し、それ以外の時だけ後続処理を行うように1文入れます。
If ds.Tables("loginResponse").Rows(0).Item("responseStatus") = "NG" Then
MessageBox.Show("プレ認証に失敗しました。")
Return
End If
応答XMLの内容を解釈するのにやや長いコードを記述しなくてはならず、しかも内容が
少し複雑なので煩雑なソースになってしまいます。
なので、個人的には
DataSetクラスを継承して独自のプロパティを持たせたDataSetを
作成する事をお勧めします。
以下はログイン応答についてのDataSetの例ですが、理解出来る方はこのように作られては
どうでしょうか?私の提案です。
Public Class LoginDataSet
Inherits DataSet
ReadOnly Property 応答結果() As String
Get
Return Me.Tables("loginResponse").Rows(0).Item("responseStatus")
End Get
End Property
ReadOnly Property 応答メッセージ() As String
Get
Return Me.Tables("loginResponse").Rows(0).Item("message")
End Get
End Property
End Class
話が少し脱線してしまいました。
ではログインシーケンス3を行います。
そのためには
「基底URL」なるものを取得しなければなりません。
仕様書にはログインシーケンス1での応答ヘッダーに含まれていると書いてありますが、
プログラムでは先ほどのレスポンス情報の中にちゃんと持っています。
基底URLは、これ以降の全てのWebサービス呼び出しに必要な情報ですので、ずっと
保持しておく必要があります。
以前定義した「クッキー」と同様、クラス変数として定義します。
Private Shared baseUrl As String = ""
基底URLは、仕様書の7ページに記載されたURLの赤色部分です。
という事は後ろについている「/to~」という部分は不要で、応答URLから切り取らないと
いけません。その作業は以下のように記述します。
baseUrl = res.ResponseUri.ToString.Replace("/to~", "")
もちろん
「"/to~"」の部分は、仕様書に書いている正式な文字列を書いて下さい。
やってる作業は、応答URIを文字列に変換し、不要な部分を空文字に変換する事です。
ログインシーケンス3はこの
「baseUrl」を使ってこのようになります。
WebResponseの変数「res」は使い回します。
res = HttpPost(baseUrl & "/ws-login", "j_username=xxxxxx&j_password=xxxxxx")
認証処理なのでユーザーIDとパスワードが必要です。
xxxxxxの部分には、それぞれ正しいユーザーIDとパスワードを記述して下さい。
このリクエスト送信で、ログインシーケンス5まで全て完了してしまいます。
結果XMLはレスポンスにちゃんと含まれています。
プレ認証の結果はプログラムが解釈する必要がありましたが、ログイン結果は
画面にダイアログ表示する事にしましょう。
先ほどのコードを参考に、このように書けます。
ds = New DataSet
Using resStream As Stream = res.GetResponseStream()
ds.ReadXml(resStream)
End Using
MessageBox.Show(ds.Tables("loginResponse").Rows(0).Item("message"))
実行すると、ログイン処理の結果メッセージがダイアログ表示されます!
やっとログイン処理の実装が終わりました。
でも、ログイン処理が一番ややこしいと私は思っています。
これが実装出来たら、あとの処理はこの応用です。
次回以降は余力情報の取得など、仕様書の「株式取引編」から簡単な要求について
解説するつもりです。
ログイン処理のソースは最終的にこのようになりました。
もしご不明な点や、ご意見・ご指摘等があれば遠慮なくコメント下さい。
宜しくお願いします。