SSブログ

[VBScript]GET/POST(疑似REST)を、Basic認証付きで複数のパラメータ/ファイルをセットして実行するクラス [プログラミング]

vbsでPOST/GETとかファイルのアップロード/ダウンロードを実装したかったのでメモ

素直にcurlコマンドを使えばよいが、runコマンドでvbs中に実行結果を取得したいときに
一度ファイルにリダイレクトして読み込まなければならないからスマートではないし、
ファイル書き出しにラグがあるから途中で読み込むと正しくない結果を取得することになる。

スマートな書き方が難しく、色々な人が試行錯誤しているのを調べてわかったが、
主に参考にさせていただいたのは以下のページ
VBScript で webclient.UploadFile もどきを作ってみた
ローカルにある画像をtumblrにPOSTするスクリプトを書いた

基本的には上記ページのコードをそのまま流用しているが、使い勝手を自分用に合わせたクラスとした。
・POST/GETをRESTのClientっぽく呼べるようにする
・渡すパラメータは複数可能とする
・POSTではファイルも複数渡せる
・BASIC認証のページに対応

以下ソースコード


Class RestClient
 Private TYPE_BINARY
 Private TYPE_TEXT
 Private CHARSET
 Private BOUNDARY_DETECTOR
 Private BOUNDARYSTR
 Private BOUNDARY
 Private END_BOUNDARY

 Private Sub Class_Initialize
  TYPE_BINARY = 1
  TYPE_TEXT = 2
  BOUNDARY_DETECTOR = "--"
  ChangeBoundary
  CHARSET = "UTF-8"
 End Sub

 Function REQ_POST(address, basicDic, paramDic, fileDic)
  Set REQ_POST = Request("POST", address, basicDic, paramDic, fileDic)
 End Function
 
 
 Function REQ_GET(address, basicDic, paramDic)
  Set REQ_GET = Request("GET", address, basicDic, paramDic, Nothing)
 End Function
 
 Private Function Request(method, address, basicDic, paramDic, fileDic)
  Dim xmlHttp: Set xmlHttp = CreateObject("Msxml2.XmlHttp")
  Dim id: id = ""
  Dim password: password = ""
  Dim key : key = ""
  If Not basicDic Is Nothing Then
   If basicDic.Count = 1 Then
    For Each key In basicDic
     id = key
     password = basicDic.Item(key)
    Next
   End If
  End If
  ChangeBoundary
  Select Case method
   Case "POST"
    With xmlHttp
     If id <> "" Then
      .Open method, address, False, id, password
     Else
      .Open method, address, False
     End If
     .setRequestHeader "Content-Type", "multipart/form-data; boundary=" + BOUNDARYSTR
     .send MakeFormData(paramDic, fileDic)
    End With
   Case "GET"
    Dim reqAddress: reqAddress = address
    If Not paramDic Is Nothing And Not IsEmpty(paramDic) Then
     Dim paramPairs: paramPairs = ""
     For Each key In paramDic
      paramPairs = paramPairs & "&" & key & "=" & paramDic.Item(key)
     Next
     reqAddress = reqAddress & "?" & Mid(paramPairs, 2)
    End If
    With xmlHttp
     If id <> "" Then
      .Open method, reqAddress, False, id, password
     Else
      .Open method, reqAddress, False
     End If
     .send
    End With
   Case Else
    Set xmlHttp = Nothing
  End Select
  Set Request = xmlHttp
 End Function
 
 
 Private Sub ChangeBoundary
  BOUNDARYSTR = MakeBoundary(30)
  BOUNDARY = BOUNDARY_DETECTOR & BOUNDARYSTR
  END_BOUNDARY = BOUNDARY & BOUNDARY_DETECTOR & vbCrLf
 End Sub
 
 Private Function MakeBoundary(digit)
  Dim hexStr: hexStr = ""
  Dim i: i = 0
  For i = 0 To digit - 1
   Randomize
   hexStr = hexStr & Hex(Int(65535 * Rnd()) Mod 16)
  Next
  MakeBoundary = "-----------------------------" & LCase(hexStr)
 End Function
 
 Private Function MakeFormData(paramDic, fileDic)
  Dim formData: formData = ""
  Dim stream: Set stream = CreateObject("Adodb.Stream")
  With stream
   .Open
   AddBegin stream
   If Not paramDic Is Nothing Then
    AddParameters stream, paramDic
   End If
   If Not fileDic Is Nothing Then
    AddFiles stream, fileDic
   End If
   AddEnd stream
   formData = ReadStream(stream)
   .Close
  End With
  MakeFormData = formData
  Set stream = Nothing
 End Function
 
 Private Sub AddBegin(stream)
  With stream
  .Type = TYPE_TEXT
  .Charset = CHARSET
  .WriteText "--"
  End With
 End Sub

 Private Sub AddParameters(stream, paramDic)
  Dim key : key = ""
  ResetPosition stream, TYPE_TEXT
  For Each key In paramDic
   stream.WriteText MakeParameter(key, paramDic.Item(key))
  Next
 End Sub
 
 Private Sub ResetPosition(stream, streamType)
  Dim currentPosition: currentPosition = stream.Position
  With stream
   .Position = 0
   .Type = streamType
   .Position = currentPosition
  End With
 End Sub
 
 Private Function MakeParameter(key, value)
  Dim parameter: parameter = ""
  If key <> "" Then
   parameter = BOUNDARY & vbCrLf & "Content-Disposition: form-data; name=""" & key  & """" & _
   vbCrLf & vbCrLf & value & vbCrLf
  End If
  MakeParameter = parameter
 End Function

 Private Sub AddFiles(stream, fileDic)
  Dim key : key = ""
  Dim fsObj: Set fsObj = CreateObject("Scripting.FileSystemObject")
  With stream
   For Each key In fileDic
    ResetPosition stream, TYPE_TEXT
    .WriteText MakeFileHeader(key, fsObj.getFileName(fileDic.Item(key)))
    ResetPosition stream, TYPE_BINARY
    .Write MakeFileStream(fileDic.Item(key))
    ResetPosition stream, TYPE_TEXT
    .WriteText vbCrLf
   Next
  End With
  Set fsObj = Nothing
 End Sub
 
 Private Function MakeFileHeader(key, filename)
  Dim header: header = ""
  If key <> "" Then
   header = BOUNDARY & vbCrLf & "Content-Disposition: form-data; name=""" & key & """; filename=""" & filename & """" & vbCrLf & _
   "Content-Type: application/octet-stream" & vbCrLf & vbCrLf
  End If
  MakeFileHeader = header
 End Function
 
 Private Function MakeFileStream(filePath)
  Dim fileContents: fileContents = ""
  Dim fileStream: Set fileStream = CreateObject("Adodb.Stream")
  With fileStream
   .Type = TYPE_BINARY
   .Open
   .LoadFromFile filePath
   fileContents = fileStream.Read
   .Close
  End With
  MakeFileStream = fileContents
  Set fileStream = Nothing
 End Function
 
 Private Sub AddEnd(stream)
  ResetPosition stream, TYPE_TEXT
  stream.WriteText END_BOUNDARY
 End Sub
 
 Private Function ReadStream(stream)
  ResetPosition stream, TYPE_BINARY
  stream.Position = 0
  ReadStream = stream.Read
 End Function
 
 Private Sub Class_Terminate
 End Sub
End Class


使い方は以下のような感じ。


'ベーシック認証/渡すパラメータ/渡すファイルのパラメータの連想配列を宣言
Dim basicDic: Set basicDic = CreateObject("Scripting.Dictionary")
Dim getParamDic: Set getParamDic = CreateObject("Scripting.Dictionary")
Dim postParamDic: Set postParamDic = CreateObject("Scripting.Dictionary")
Dim postFileDic: Set postFileDic = CreateObject("Scripting.Dictionary")

'RestClientクラス自体の記述は省略
Dim client: Set client = new RestClient

'リクエストを送るURL
Const url = "http://test.test/"

'BASIC認証の情報を追加(2つ以上追加すると無効)
basicDic.Add "myusername", "mypassword"


'GETの例
'パラメータの組み合わせを2つ追加
getParamDic.Add "getParamKey1", "getParamValue1"
getParamDic.Add "getParamKey2", "getParamValue2"

'GET実行
Dim getResult: Set getResult = client.REQ_GET(url, basicDic, getParamDic)
'結果表示
Wscript.Echo getResult.responseText


'POSTの例
'パラメータの組み合わせを2つ追加し、ファイルも2つ追加
postParamDic.Add "postParamKey1", "postParamValue1"
postParamDic.Add "postParamKey2", "postParamValue2"
postFileDic.Add "postFileKey1", "C:\temp\test1.txt"
postFileDic.Add "postFileKey2", "C:\temp\test2.txt"

'POST実行
Dim postResult: Set postResult = client.REQ_POST(url, Nothing, postParamDic, postFileDic)
'結果表示
Wscript.Echo postResult.responseText

Set getResult = Nothing
Set client = Nothing
Set postFileDic = Nothing
Set postParamDic = Nothing
Set getParamDic = Nothing
Set basicDic = Nothing

Wscript.Quit



BASIC認証が必要ないURLの場合は、REQ_POST/REQ_GETの第二引数にNothingを指定する
その他パラメータやファイルも必要がなければ同様に第三、四引数にNothingを指定する

上記実装例をリクエストの生データそのまま返すURLに送ったときの結果
GET
01.png

POST
02.png
GET/POSTのswitch文で似たようなMsxml2.XmlHttpを
重複して書いているので、美しくないが良い方法が思いつかなかった。
ちなみに、GETでファイルをダウンロードする場合はREQ_GETの取得後以下のようにすれば良い。


Dim adodbStream: Set adodbStream = CreateObject("Adodb.Stream")
 With adodbStream
  .Open
  .Type = 1
  .Write getResult.responseBody
  .SaveToFile "C:\temp\save.txt", 2
  .Close
End With

とりあえず呼び出し側を極力スマートに書けるようにしたので一旦ここまで。

以上。

nice!(1)  コメント(0) 
共通テーマ:パソコン・インターネット

nice! 1

コメント 0

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。