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