VBA 的應用場景基本都仍是在單機應用, 隨着 Web 應用的風靡, 以及瀏覽器愈來愈強大, 單機類的應用逐漸沒落.git
雖然 Web 應用愈來愈多, 功能和體驗也愈來愈好, 可是 Excel 依然有很強的生命力, 畢竟 Web 頁面上的表格再強大, 也沒有 Excel 那麼方便易用.github
Excel 通過這麼多年的積累, 不只有很大的用戶基礎, 對錶格類數據的處理幾乎已經到極致.
若是可以以 Excel 自己做爲 UI, 經過 VBA 來鏈接 Excel 和後端服務, 使它成爲 Web 頁面的輔助, 而不是強行在 Web 頁面實現 Excel 表格的各類功能, 應該能有事半功倍的效果.golang
先後端分離已是目前的主流, 只要可以經過 VBA 訪問後端的 API, 並解析後端返回的數據(通常是 JSON 格式), 就能讓 Excel 成爲 Web 頁面的輔助, 高效的處理表格類數據.json
爲了能和後端 API 交互, 須要實現一個簡單的 httplib 庫, 便於在各個 VBA 工程中複用.後端
根據目前本身的需求, 這個簡單的 httplib 庫暫時完成如下幾個功能:瀏覽器
爲了測試 httplib, 用 golang 的 gin 框架簡單實現了一個 http API 服務. 代碼參見文後的 附錄一緩存
httplib 主要實現了 get/post 訪問 API, 參數和返回值都是 json 格式字符串. 代碼參見文後的 附錄二cors
下面的測試主要演示如何使用 httplib :框架
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"}
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 數據, 這裏藉助另一個 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
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 加入信息很簡單, 在 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
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 }
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