概要
VBA 的應用場景基本都還是在單機應用, 隨着 Web 應用的風靡, 以及瀏覽器越來越強大, 單機類的應用逐漸沒落.
雖然 Web 應用越來越多, 功能和體驗也越來越好, 但是 Excel 依然有很強的生命力, 畢竟 Web 頁面上的表格再強大, 也沒有 Excel 那么方便易用.
Excel 經過這么多年的積累, 不僅有很大的用戶基礎, 對表格類數據的處理幾乎已經到極致.
如果能夠以 Excel 本身作為 UI, 通過 VBA 來連接 Excel 和后端服務, 使它成為 Web 頁面的輔助, 而不是強行在 Web 頁面實現 Excel 表格的各種功能, 應該能有事半功倍的效果.
前后端分離已經是目前的主流, 只要能夠通過 VBA 訪問后端的 API, 並解析后端返回的數據(一般是 JSON 格式), 就能讓 Excel 成為 Web 頁面的輔助, 高效的處理表格類數據.
VBA httplib 庫的簡單實現
為了能和后端 API 交互, 需要實現一個簡單的 httplib 庫, 便於在各個 VBA 工程中復用.
根據目前自己的需求, 這個簡單的 httplib 庫暫時完成以下幾個功能:
- 能夠發送 GET 請求
- 能夠發送 POST 請求
- 能夠解析請求返回的 JSON 數據
- POST 請求中可以帶 JSON 格式的參數
- 請求的 header 中可以加入 jwt token 信息
環境准備
為了測試 httplib, 用 golang 的 gin 框架簡單實現了一個 http API 服務. 代碼參見文后的 附錄一
httplib 主要實現了 get/post 訪問 API, 參數和返回值都是 json 格式字符串. 代碼參見文后的 附錄二
下面的測試主要演示如何使用 httplib :
發送 GET 請求
1 Function testGet()
2 Dim hlib As New HttpLib
3 Dim ret As Boolean
4
5 hlib.SetUrl = "http://localhost:8000/get-test"
6 ret = hlib.HttpGetJSON("")
7 Debug.Print ret
8 If ret = True Then
9 Debug.Print hlib.GetJSONResp
10 End If
11 End Function
SetUrl 之后, 調用 HttpGetJSON 即可
運行結果如下:
True
{"message":"get success"}
發送 POST 請求
1 Function testPost()
2 Dim hlib As New HttpLib
3 Dim ret As Boolean
4
5 hlib.SetUrl = "http://localhost:8000/post-test"
6 ret = hlib.HttpPostJSON("")
7 Debug.Print ret
8 If ret = True Then
9 Debug.Print hlib.GetJSONResp
10 End If
11 End Function
SetUrl 之后, 調用 HttpPostJSON 即可
運行結果如下:
True
{"message":"post success"}
解析請求返回的 JSON 數據
要解析返回的 JSON 數據, 這里借助另外一個 VBA 模塊: VBA-JSON
1 Function testPostWithReturn()
2
3 Dim hlib As New HttpLib
4 Dim ret As Boolean
5
6 hlib.SetUrl = "http://localhost:8000/post-test-return"
7 ret = hlib.HttpPostJSON("")
8 Debug.Print ret
9 If ret = True Then
10 Dim resp As Object
11 Set resp = JsonConverter.ParseJson(hlib.GetJSONResp)
12 Debug.Print "response json: " & hlib.GetJSONResp
13 Debug.Print "username: " & resp("username")
14 Debug.Print "data -> list 1: " & resp("data")("list")(1)
15 Debug.Print "data -> list 2: " & resp("data")("list")(2)
16 Debug.Print "data -> list 3: " & resp("data")("list")(3)
17 End If
18 End Function
注 在使用 VBA-JSON 庫的時候, 需要添加引用 Microsoft Scripting Runtime
添加的方法, 在 VBA 編輯器中 選擇 "工具" -> "引用" -> 添加 "Microsoft Scripting Runtime"
運行結果如下:
True
response json: {"data":{"list":["a","b","c"]},"username":"string"}
username: string
data -> list 1: a
data -> list 2: b
data -> list 3: c
POST 請求中帶 JSON 格式的參數
1 Function testPostWithParam()
2
3 Dim hlib As New HttpLib
4 Dim ret As Boolean
5 Dim p As Dictionary
6 Set p = New Dictionary
7
8 hlib.SetUrl = "http://localhost:8000/post-test-param"
9 p("name") = "name"
10 p("data") = Array("a", "b", "c")
11
12 ret = hlib.HttpPostJSON(JsonConverter.ConvertToJson(p))
13 Debug.Print ret
14 If ret = True Then
15 Debug.Print "response json: " & hlib.GetJSONResp
16 End If
17 End Function
在服務端的 log 中, 可以看到, 執行后獲取到了傳遞的參數
2019/10/09 12:14:38 param: struct { Name string "json:\"name\""; Data []string "json:\"data\"" }{Name:"name", Data:[]string{"a", "b", "c"}}
請求的 header 中加入 jwt token 信息
請求中 header 加入信息很簡單, 在 httplib 庫中的 HttpGetJSON 和 HttpPostJSON 方法中都有:
1 http.setRequestHeader "Content-Type", "text/json"
2 http.setRequestHeader "If-Modified-Since", "0" ' 清除緩存
3 If jwtToken <> "" Then
4 http.setRequestHeader "Authorization", "Bearer " & jwtToken
5 End If
附錄
附錄一 測試用服務端(by golang)
1 package main
2
3 import (
4 "log"
5 "net/http"
6
7 "github.com/gin-contrib/cors"
8 "github.com/gin-gonic/gin"
9 )
10
11 func StartGinServ() {
12 r := gin.Default()
13 r.Use(cors.Default())
14
15 r.GET("/get-test", func(c *gin.Context) {
16 c.JSON(http.StatusOK, gin.H{
17 "message": "get success",
18 })
19 })
20
21 r.POST("/post-test", func(c *gin.Context) {
22 c.JSON(http.StatusOK, gin.H{
23 "message": "post success",
24 })
25 })
26
27 var list = []string{"a", "b", "c"}
28 r.POST("/post-test-return", func(c *gin.Context) {
29 c.JSON(http.StatusOK, gin.H{
30 "username": "string",
31 "data": gin.H{
32 "list": list,
33 },
34 })
35 })
36
37 r.POST("/post-test-param", func(c *gin.Context) {
38 var param struct {
39 Name string `json:"name"`
40 Data []string `json:"data"`
41 }
42
43 if err := c.BindJSON(¶m); err != nil {
44 log.Fatal("param error")
45 }
46
47 log.Printf("param: %#v\n", param)
48
49 c.JSON(http.StatusOK, gin.H{
50 "message": "post with param",
51 })
52 })
53
54 if err := r.Run(":8000"); err != nil {
55 log.Fatal(err)
56 }
57
58 }
附錄二 完整的 httplib 庫
1 Option Explicit
2
3 Const ClsName = "lib for http request"
4
5 ' 請求的URL
6 Dim url As String
7 ' API認證用的 jwt token
8 Dim jwtToken As String
9 ' API返回的json格式結果
10 Dim jsonResp As String
11
12 ' 設置 URL 屬性
13 Public Property Let SetUrl(p As String)
14 url = p
15 End Property
16
17 ' 獲取 URL 屬性
18 Public Property Get GetUrl() As String
19 GetUrl = url
20 End Property
21
22 ' 設置 jwt token 屬性
23 Public Property Let SetJwtToken(p As String)
24 jwtToken = p
25 End Property
26
27 ' 獲取 jwt token 屬性
28 Public Property Get GetJwtToken() As String
29 GetJwtToken = jwtToken
30 End Property
31
32 ' 獲取 json response 屬性
33 Public Property Get GetJSONResp() As String
34 GetJSONResp = jsonResp
35 End Property
36
37 ' TODO 登錄操作, 登錄成功后, 設置 jwt token
38 Function Login(user As String, pwd As String) As Boolean
39 ' TODO 這里可以根據具體的登錄實現方式來實現
40 ' 登錄成功后, 設置 jwtToken 屬性
41 Login = False
42 End Function
43
44 ' GET 方式訪問 API
45 Function HttpGetJSON(jsonStr As String) As Boolean
46 HttpGetJSON = False
47 jsonResp = ""
48
49 Dim http
50 Set http = CreateObject("Msxml2.XMLHTTP")
51 http.Open "GET", url, False
52
53 ' 設置headers
54 http.setRequestHeader "Content-Type", "text/json"
55 http.setRequestHeader "If-Modified-Since", "0" ' 清除緩存
56 If jwtToken <> "" Then
57 http.setRequestHeader "Authorization", "Bearer " & jwtToken
58 End If
59
60 http.send jsonStr
61
62 If http.Status = 200 Then
63 jsonResp = http.responseText
64 HttpGetJSON = True
65 End If
66
67 End Function
68
69 ' POST 方式訪問 API
70 Function HttpPostJSON(jsonStr As String) As Boolean
71 HttpPostJSON = False
72 jsonResp = ""
73
74 Dim http
75 Set http = CreateObject("Msxml2.XMLHTTP")
76 http.Open "POST", url, False
77
78 http.setRequestHeader "Content-Type", "text/json"
79 http.setRequestHeader "If-Modified-Since", "0" ' 清除緩存
80 If jwtToken <> "" Then
81 http.setRequestHeader "Authorization", "Bearer " & jwtToken
82 End If
83
84 http.send jsonStr
85
86 If http.Status = 200 Then
87 jsonResp = http.responseText
88 HttpPostJSON = True
89 End If
90 End Function