百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

用 Golang封装你的API(golang封装dll)

liuian 2025-05-09 20:03 54 浏览

每日分享最新,最流行的软件开发知识与最新行业趋势,希望大家能够一键三连,多多支持,跪求关注,点赞,留言。

@头条创作挑战赛

本文探讨了在 用 Golang封装你的API的过程以及几个不同的编程步骤。

我做了一个非常有限的时间来证明如何为客户正在开发的开放 API 编写命令行包装器。
目标 REST API 是jquants-api,如前 一篇文章中所述。
我选择在 Golang 中实现封装,事实证明这非常快速且令人愉快。该任务最终在一个短暂的晚上完成,生成的具有核心功能的 Golang 的封装已上传到GitHub 上。
这是关于编写 API 的过程和几个不同的编程步骤的简短故事。
目标
首先,让我们列出我们必须处理的编程任务:
创建一个测试和支持代码,检查我们可以将用户名和密码保存在与 jquants-api-jvm 格式兼容的 edn 文件中
编写另一个测试和支持代码来检索刷新令牌
编写另一个测试和支持代码来检索 ID 令牌
使用 ID 令牌编写另一个测试和支持代码以检索每日值
将我们的包装器发布到 GitHub
在另一个程序中使用我们的 Go 库
首先编写测试用例,准备并保存登录结构以访问 API
我们总是谈论使用 TDD 编写代码——现在是时候这样做了。检查我们是否有代码可以输入用户名和密码并将其保存在与 jquants-api-jvm 格式兼容的 edn 文件中。
在 helper_test.go 文件中,让我们为PrepareLogin函数编写框架测试。

package jquants_api_go
import (
"fmt"
"os"
"testing"
)
func TestPrepareLogin(t *testing.T) {
PrepareLogin(os.Getenv("USERNAME"), os.Getenv("PASSWORD"))
}

在这里,我们从环境中获取 USERNAME 和 PASSWORD,使用os.GetEnv.
我们将准备函数写在一个helper.go文件中。它会:
获取用户名和密码作为参数
实例化一个登录结构
将其编组为 EDN 文件内容
func PrepareLogin(username string, password string) {
var user = Login{username, password}
encoded, _ := edn.Marshal(&user)
writeConfigFile("login.edn", encoded)
}
我们的 Login 结构首先将是:
type Login struct {
UserName string `edn:"mailaddress"`
Password string `edn:"password"`
}
调用edn.Marshal将创建一个 byte[] 数组内容,我们可以将其写入文件,因此writeConfigFile只需os.WriteFile使用从 EDN 编组返回的数组进行调用。
func writeConfigFile(file string, content []byte) {
os.WriteFile(getConfigFile(file), content, 0664)
}
为了能够使用 EDN 库,我们需要将其添加到go.mod文件中:
require olympos.io/encoding/edn
v0.0.0-20201019073823-d3554ca0b0a3

在运行测试之前,一定要输入你的 jquants API 的凭证:
export USERNAME="youremail@you.com"
export PASSWORD="yourpassword"
在这个阶段,你应该可以go test在项目文件夹中运行,并看到以下输出:
PASS
ok
github.com/hellonico/jquants-api-go 1.012s

您还应该看到login.edn文件的内容已正确填充:
cat
~/.config/jquants/login.edn

{:mailaddress "youremail@you.com" :password "yourpassword"}
使用登录向 jQuants API 发送 HTTP 请求并检索 RefreshToken
要测试的第二个函数是TestRefreshToken,它使用用户名和密码发送 HTTP 发布请求,并检索刷新令牌作为 API 调用的答案。我们helper_test.go使用新的测试用例更新文件:
func TestRefreshToken(t *testing.T) {
token, _ := GetRefreshToken()
fmt.Printf("%s\n", token)
}
该GetRefreshToken函数将:
加载先前存储在文件中的用户并将其准备为 JSON 数据
使用 URL 和 JSON 格式的用户作为正文内容准备 HTTP 请求
发送 HTTP 请求
API 将返回将存储在 RefreshToken 结构中的数据
让我们将该刷新令牌存储为 EDN 文件
支持GetUser现在将加载在之前的步骤中写入的文件内容。我们已经有了Login结构,然后将只使用edn.Unmarshall() 文件中的内容。
func GetUser() Login {
s, _ := os.ReadFile(getConfigFile("login.edn"))
var user Login
edn.Unmarshal(s, &user)
return user
}
请注意,虽然我们希望将 Login 结构读/写到 EDN 格式的文件中,但我们还希望在发送 HTTP 请求时将结构编组为 JSON。
所以我们的 Login 结构上的元数据需要稍微更新一下:
type Login struct {
UserName string `edn:"mailaddress" json:"mailaddress"`
Password string `edn:"password" json:"password"`
}
我们还需要一个新结构来读取 API 返回的令牌,并且我们还希望将其存储为 EDN,就像我们为Login结构所做的那样:
type RefreshToken struct {
RefreshToken string `edn:"refreshToken" json:"refreshToken"`
}
现在,我们拥有了编写GetRefreshToken函数的所有内容:
func GetRefreshToken() (RefreshToken, error) {
// load user stored in file previously and prepare it as json data
var user = GetUser()
data, err := json.Marshal(user)
// prepare the http request, with the url, and the json formatted user as body content
url := fmt.Sprintf("%s/token/auth_user", BASE_URL)
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data))

// send the request
client := http.Client{}
res, err := client.Do(req)
// the API will returns data that will store in a RefreshToken struct
var rt RefreshToken
json.NewDecoder(res.Body).Decode(&rt)
// and let's store that refresh token as an EDN file
encoded, err := edn.Marshal(&rt)
writeConfigFile(REFRESH_TOKEN_FILE, encoded)
return rt, err
}
运行go test有点冗长,因为我们将 refreshToken 打印到标准输出,但测试应该通过了!
{eyJjdHkiOiJKV1QiLC...}
PASS
ok
github.com/hellonico/jquants-api-go 3.231s

获取 ID 令牌
从刷新令牌中,您可以检索 IdToken,它是用于向 jquants API 发送请求的令牌。这与 .it 具有几乎相同的流程GetRefreshToken,为了支持它,我们主要引入了一个新结构IdToken,其中包含必要的元数据来编组到 edn/json 或从 edn/json 编组。
type IdToken struct {
IdToken string `edn:"idToken" json:"idToken"`
}
这次剩下的代码是:
func GetIdToken() (IdToken, error) {
var token = ReadRefreshToken()
url := fmt.Sprintf("%s/token/auth_refresh?refreshtoken=%s", BASE_URL, token.RefreshToken)
req, err := http.NewRequest(http.MethodPost, url, nil)
client := http.Client{}
res, err := client.Do(req)
var rt IdToken
json.NewDecoder(res.Body).Decode(&rt)
encoded, err := edn.Marshal(&rt)
writeConfigFile(ID_TOKEN_FILE, encoded)
return rt, err
}
获取每日行情
我们来到了包装代码的核心,在这里我们使用 IdToken,并通过 HTTP GET 请求从 jquants HTTP API 中请求每日报价。
检索每日报价的代码流程是:
和以前一样,从 EDN 文件中读取 ID 令牌
使用参数 code 和 dates 参数准备目标 URL
使用 idToken 作为 HTTP 标头发送 HTTP 请求
将结果解析为每日报价结构,它是报价结构的切片
测试用例只是检查返回的非空值并暂时打印引号。
func TestDaily(t *testing.T) {
var quotes = Daily("86970", "", "20220929", "20221003")

if quotes.DailyQuotes == nil {
t.Failed()
}
for _, quote := range quotes.DailyQuotes {
fmt.Printf("%s,%f\n", quote.Date, quote.Close)
}
}
的支持代码func Daily如下所示:
func Daily(code string, date string, from string, to string) DailyQuotes {
// read id token
idtoken := ReadIdToken()
// prepare url with parameters
baseUrl := fmt.Sprintf("%s/prices/daily_quotes?code=%s", BASE_URL, code)
var url string
if from != "" && to != "" {
url = fmt.Sprintf("%s&from=%s&to=%s", baseUrl, from, to)
} else {
url = fmt.Sprintf("%s&date=%s", baseUrl, date)
}
// send the HTTP request using the idToken
res := sendRequest(url, idtoken.IdToken)
// parse the result as daily quotes
var quotes DailyQuotes
err_ := json.NewDecoder(res.Body).Decode("es)
Check(err_)
return quotes
}
现在我们需要填写一些空白:
sendRequest 需要更多细节
DailyQuotes的解析其实并没有那么简单
所以,首先让我们把 sendRequest 函数排除在外。它使用 设置标题http.Header,并注意您可以在此处添加任意数量的标题。然后它发送 HTTP GET 请求并按原样返回响应。
func sendRequest(url string, idToken string) *http.Response {
req, _ := http.NewRequest(http.MethodGet, url, nil)
req.Header = http.Header{
"Authorization": {"Bearer " + idToken},
}
client := http.Client{}
res, _ := client.Do(req)
return res
}
现在来解析每日报价。如果您使用 Goland 作为您的编辑器,您会注意到,如果您将 JSON 内容复制粘贴到您的 Go 文件中,编辑器将要求直接将 JSON 转换为 Go 代码!
挺整洁的。
type Quote struct {
Code string `json:"Code"`
Close float64 `json:"Close"`
Date JSONTime `json:"Date"`
AdjustmentHigh float64 `json:"AdjustmentHigh"`
Volume float64 `json:"Volume"`
TurnoverValue float64 `json:"TurnoverValue"`
AdjustmentClose float64 `json:"AdjustmentClose"`
AdjustmentLow float64 `json:"AdjustmentLow"`
Low float64 `json:"Low"`
High float64 `json:"High"`
Open float64 `json:"Open"`
AdjustmentOpen float64 `json:"AdjustmentOpen"`
AdjustmentFactor float64 `json:"AdjustmentFactor"`
AdjustmentVolume float64 `json:"AdjustmentVolume"`
}
type DailyQuotes struct {
DailyQuotes []Quote `json:"daily_quotes"`
}
虽然默认值非常好,但我们需要做更多的调整以正确解组日期。以下内容来自以下关于如何编组/解组 JSON 日期的帖子。
JSONTime 类型会将其内部日期存储为 64 位整数,我们将函数添加到 JSONTime 以编组/解组 JSONTime。如图所示,来自 JSON 内容的时间值可以是字符串或整数。
type JSONTime int64
// String converts the unix timestamp into a string
func (t JSONTime) String() string {
tm := t.Time()
return fmt.Sprintf("\"%s\"", tm.Format("2006-01-02"))
}
// Time returns a `time.Time` representation of this value.
func (t JSONTime) Time() time.Time {
return time.Unix(int64(t), 0)
}
// UnmarshalJSON will unmarshal both string and int JSON values
func (t *JSONTime) UnmarshalJSON(buf []byte) error {
s := bytes.Trim(buf, `"`)
aa, _ := time.Parse("20060102", string(s))
*t = JSONTime(aa.Unix())
return nil
}
最初编写的测试用例现在应该通过go test.
"2022-09-29",1952.000000
"2022-09-30",1952.500000
"2022-10-03",1946.000000
PASS
ok
github.com/hellonico/jquants-api-go 1.883s

我们的助手现在已经准备好了,我们可以向它添加一些 CI。
CircleCI 配置
配置是字符到字符的,接近于使用 Golang 进行测试的官方 CircleCI 文档。
我们只需将 Docker 映像更新为1.17.
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: cimg/go:1.17.9
steps:
- checkout
- restore_cache:
keys:
- go-mod-v4-{{ checksum "go.sum" }}
- run:
name: Install Dependencies
command: go get ./...
- save_cache:
key: go-mod-v4-{{ checksum "go.sum" }}
paths:
- "/go/pkg/mod"
- run: go test -v
现在我们准备在 CircleCI 上设置项目:
我们的 helper_test.go 中所需的参数 USERNAME 和 PASSWORD 可以直接从 CircleCI 项目的环境变量设置中设置:
主分支上的任何提交都会触发 CircleCI 构建(当然,您也可以手动触发它),如果一切顺利,您应该会看到成功的步骤:
我们的包装是经过良好测试的。让我们开始发布它。
在 GitHub 上发布库
提供我们的 go.mod 文件具有以下内容:
module
github.com/hellonico/jquants-api-go

go 1.17
require olympos.io/encoding/edn
v0.0.0-20201019073823-d3554ca0b0a3

发布代码的最佳方式是使用 git 标签。因此,让我们创建一个 git 标签并将其推送到 GitHub:
git tag v0.6.0
git push --tags
现在,一个单独的项目可以通过在他们的go.mod.
require
github.com/hellonico/jquants-api-go v0.6.0

从外部程序使用库
我们的简单程序将使用标志模块解析参数,然后调用不同的函数,就像在我们的包装器的测试用例中所做的那样。
package main
import (
"flag"
"fmt"
jquants "
github.com/hellonico/jquants-api-go"

)
func main() {
code := flag.String("code", "86970", "Company Code")
date := flag.String("date", "20220930", "Date of the quote")
from := flag.String("from", "", "Start Date for date range")
to := flag.String("to", "", "End Date for date range")
refreshToken := flag.Bool("refresh", false, "refresh RefreshToken")
refreshId := flag.Bool("id", false, "refresh IdToken")
flag.Parse()
if *refreshToken {
jquants.GetRefreshToken()
}
if *refreshId {
jquants.GetIdToken()
}
var quotes = jquants.Daily(*code, *date, *from, *to)
fmt.Printf("[%d] Daily Quotes for %s \n", len(quotes.DailyQuotes), *code)
for _, quote := range quotes.DailyQuotes {
fmt.Printf("%s,%f\n", quote.Date, quote.Close)
}
}
我们可以使用go build.
go build
并在此处使用所需参数运行它:
刷新 ID 令牌
刷新刷新令牌
在 20221005 和 20221010 之间获取代码为 86970 的实体的每日值
./jquants-example --id --refresh --from=20221005 --to=20221010 --code=86970
Code: 86970 and Date: 20220930 [From: 20221005 To: 20221010]
[3] Daily Quotes for 86970
"2022-10-05",2016.500000
"2022-10-06",2029.000000
"2022-10-07",1992.500000
不错的作品。我们将把它留给用户编写其余的statements,listedInfo它们是 JQuants API 的一部分,但尚未在此包装器中实现。

相关推荐

驱动网卡(怎么从新驱动网卡)
驱动网卡(怎么从新驱动网卡)

网卡一般是指为电脑主机提供有线无线网络功能的适配器。而网卡驱动指的就是电脑连接识别这些网卡型号的桥梁。网卡只有打上了网卡驱动才能正常使用。并不是说所有的网卡一插到电脑上面就能进行数据传输了,他都需要里面芯片组的驱动文件才能支持他进行数据传输...

2026-01-30 00:37 liuian

win10更新助手装系统(微软win10更新助手)

1、点击首页“系统升级”的按钮,给出弹框,告诉用户需要上传IMEI码才能使用升级服务。同时给出同意和取消按钮。华为手机助手2、点击同意,则进入到“系统升级”功能华为手机助手华为手机助手3、在检测界面,...

windows11专业版密钥最新(windows11专业版激活码永久)

 Windows11专业版的正版密钥,我们是对windows的激活所必备的工具。该密钥我们可以通过微软商城或者通过计算机的硬件供应商去购买获得。获得了windows11专业版的正版密钥后,我...

手机删过的软件恢复(手机删除过的软件怎么恢复)
手机删过的软件恢复(手机删除过的软件怎么恢复)

操作步骤:1、首先,我们需要先打开手机。然后在许多图标中找到带有[文件管理]文本的图标,然后单击“文件管理”进入页面。2、进入页面后,我们将在顶部看到一行文本:手机,最新信息,文档,视频,图片,音乐,收藏,最后是我们正在寻找的[更多],单击...

2026-01-29 23:55 liuian

一键ghost手动备份系统步骤(一键ghost 备份)

  步骤1、首先把装有一键GHOST装系统的U盘插在电脑上,然后打开电脑马上按F2或DEL键入BIOS界面,然后就选择BOOT打USDHDD模式选择好,然后按F10键保存,电脑就会马上重启。  步骤...

怎么创建局域网(怎么创建局域网打游戏)

  1、购买路由器一台。进入路由器把dhcp功能打开  2、购买一台交换机。从路由器lan端口拉出一条网线查到交换机的任意一个端口上。  3、两台以上电脑。从交换机任意端口拉出网线插到电脑上(电脑设置...

精灵驱动器官方下载(精灵驱动手机版下载)

是的。驱动精灵是一款集驱动管理和硬件检测于一体的、专业级的驱动管理和维护工具。驱动精灵为用户提供驱动备份、恢复、安装、删除、在线更新等实用功能。1、全新驱动精灵2012引擎,大幅提升硬件和驱动辨识能力...

一键还原系统步骤(一键还原系统有哪些)

1、首先需要下载安装一下Windows一键还原程序,在安装程序窗口中,点击“下一步”,弹出“用户许可协议”窗口,选择“我同意该许可协议的条款”,并点击“下一步”。  2、在弹出的“准备安装”窗口中,可...

电脑加速器哪个好(电脑加速器哪款好)

我认为pp加速器最好用,飞速土豆太懒,急速酷六根本不工作。pp加速器什么网页都加速,太任劳任怨了!以上是个人观点,具体性能请自己试。ps:我家电脑性能很好。迅游加速盒子是可以加速电脑的。因为有过之...

任何u盘都可以做启动盘吗(u盘必须做成启动盘才能装系统吗)

是的,需要注意,U盘的大小要在4G以上,最好是8G以上,因为启动盘里面需要装系统,内存小的话,不能用来安装系统。内存卡或者U盘或者移动硬盘都可以用来做启动盘安装系统。普通的U盘就可以,不过最好U盘...

u盘怎么恢复文件(u盘文件恢复的方法)

开360安全卫士,点击上面的“功能大全”。点击文件恢复然后点击“数据”下的“文件恢复”功能。选择驱动接着选择需要恢复的驱动,选择接入的U盘。点击开始扫描选好就点击中间的“开始扫描”,开始扫描U盘数据。...

系统虚拟内存太低怎么办(系统虚拟内存占用过高什么原因)

1.检查系统虚拟内存使用情况,如果发现有大量的空闲内存,可以尝试释放一些不必要的进程,以释放内存空间。2.如果系统虚拟内存使用率较高,可以尝试增加系统虚拟内存的大小,以便更多的应用程序可以使用更多...

剪贴板权限设置方法(剪贴板访问权限)
剪贴板权限设置方法(剪贴板访问权限)

1、首先打开iphone手机,触碰并按住单词或图像直到显示选择选项。2、其次,然后选取“拷贝”或“剪贴板”。3、勾选需要的“权限”,最后选择开启,即可完成苹果剪贴板权限设置。仅参考1.打开苹果手机设置按钮,点击【通用】。2.点击【键盘】,再...

2026-01-29 21:37 liuian

平板系统重装大师(平板重装win系统)

如果你的平板开不了机,但可以连接上电脑,那就能好办,楼主下载安装个平板刷机王到你的个人电脑上,然后连接你的平板,平板刷机王会自动识别你的平板,平板刷机王上有你平板的我刷机包,楼主点击下载一个,下载完成...

联想官网售后服务网点(联想官网售后服务热线)

联想3c服务中心是联想旗下的官方售后,是基于互联网O2O模式开发的全新服务平台。可以为终端用户提供多品牌手机、电脑以及其他3C类产品的维修、保养和保险服务。根据客户需求层次,联想服务针对个人及家庭客户...