概要

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 库暂时完成以下几个功能:

  1. 能够发送 GET 请求
  2. 能够发送 POST 请求
  3. 能够解析请求返回的 JSON 数据
  4. POST 请求中可以带 JSON 格式的参数
  5. 请求的 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 库中的 HttpGetJSONHttpPostJSON 方法中都有:

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(&param); 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

VBA实战 - 一个简单的 httplib的更多相关文章

  1. [VBA]用一个简单例子说明如何在Excel中自定义函数

    Excel中的函数无疑是强大的,但是再强大的战士也有他脆弱的脚后跟[1].这两天在使用Excel的时候遇到了一个需求,要在某一个单元格里面自动计算今天是星期几(如显示 Today is Tuesday ...

  2. Spring Boot 揭秘与实战 自己实现一个简单的自动配置模块

    文章目录 1. 实战的开端 – Maven搭建 2. 参数的配置 - 属性参数类 3. 真的很简单 - 简单的服务类 4. 自动配置的核心 - 自动配置类 5. spring.factories 不要 ...

  3. 3.2 Lucene实战:一个简单的小程序

    在讲解Lucene索引和检索的原理之前,我们先来实战Lucene:一个简单的小程序! 一.索引小程序 首先,new一个java project,名字叫做LuceneIndex. 然后,在project ...

  4. 《maven实战》笔记(2)----一个简单maven项目的搭建,测试和打包

    参照<maven实战>在本地创建对应的基本项目helloworld,在本地完成后项目结构如下: 可以看到maven项目的骨架:src/main/java(javaz主代码)src/test ...

  5. HTML5实战教程———开发一个简单漂亮的登录页面

    最近看过几个基于HTML5开发的移动应用,比如臭名昭著的12036移动客户端就是主要使用HTML5来实现的,虽然还是有点反应迟钝,但已经比较流畅了,相信随着智能手机的配置越来越高性能越来越好,会越来越 ...

  6. 微信小程序实战--开发一个简单的快递单号查询

    功能如图: 虽然工作中只负责小程序后台开发,但是还是小程序开发产生了浓厚的兴趣,官方文档也是超级详细了 这里就简单做一个快递单号的查询: 新建一个page: 接着就可以写wxml了.这里用一个简单的i ...

  7. Node.js实战14:一个简单的TCP服务器。

    本文,将会展示如何用Nodejs内置的net模块开发一个TCP服务器,同时模拟一个客户端,并实现客户端和服务端交互. net模块是nodejs内置的基础网络模块,通过使用net,可以创建一个简单的tc ...

  8. 自己动手模拟开发一个简单的Web服务器

    开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...

  9. [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

    一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...

随机推荐

  1. 怎么在ubuntu下安装使用pycharm

    1.安装jdk 先下载jdk: https://pan.baidu.com/s/1o7MqvKA 解压到本地: 方法一:直接点击右键,点“提取此文件 方法二:使用命令行sudo tar -zxvf j ...

  2. php 交换值

    使用异或和第三参数比较 结果比较:(其中之一) 异或:执行时间在 0.035-0.085之间 第三参数:执行时间在 0.035-0.050之间 结论:使用第三参数执行效率更高/更稳定

  3. 跳出"低水平勤奋陷阱"

    "低水平勤奋陷阱":摘记更多的知识 读书是获得知识的最基本,最重要的方式,但读书需要方法 所谓"低水平勤奋陷阱",就是花费了大量的时间和精力,但得到的结果却微乎 ...

  4. java高并发系列 - 第5天:深入理解进程和线程

    进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础.程序是指令.数据及其组织形式的描述,进程是程序的实体. 进程具有的 ...

  5. Android Studio当中的创建新方法的快捷键该如何使用?

    当有红线出现的时候,我们的代码并没有编译出错,则需要输入alt+enter则可以得到相应的神奇效果了.这个方法我竟然今天才知道,也真是丢脸了.比如说我们书写了一个新的没有创建的方法,我们直接输入alt ...

  6. Android多module下重复jar包问题

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/166 Android多module下重复jar包问题 An ...

  7. [转]JVM系列五:JVM监测&工具[整理中]

    原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/09/2040977.html 前几篇篇文章介绍了介绍了JVM的参数设置并给出了一些生产环境的 ...

  8. Scala字符穿插值器

    从2.10.0版本开始,Scala提供了三种创新的字符串插值方法: s.f 和 raw 于2.11.0版本开始,用于模式匹配patter match中 小结: s: s"Hello, $na ...

  9. 6.python3实用编程技巧进阶(一)

    1.1.如何在列表中根据条件筛选数据 # 1.1.如何在列表中根据条件筛选数据 data = [-1, 2, 3, -4, 5] #筛选出data列表中大于等于零的数据 #第一种方法,不推荐 res1 ...

  10. SRDC - ORA-30013: Checklist of Evidence to Supply (Doc ID 1682701.1)

    Action Plan 1. Execute srdc_db_undo_ora-30013.sql as sysdba and provide the spool output --srdc_db_u ...