前言
在这篇博客中,我们将介绍如何使用Go语言和Fyne框架构建一个简易的API工具。该工具允许用户输入API URL和API Key,通过简易的GUI与API进行交互,获取账单信息、模型列表并测试模型。本项目展示了一些实用的Go编程技巧和Fyne的使用方法。
完整代码下载地址在文末!!!!
项目依赖
首先,我们需要确保已经安装了必要的依赖项:
- Go编程语言
- Fyne GUI框架
你可以通过如下命令来安装Fyne:
go get fyne.io/fyne/v2
项目结构
项目的主代码入口在main.go
文件中,其核心组件包括以下几部分:
- GUI组件初始化与布局
- API请求及响应处理
- 输入验证
- 结果展示
主函数
主函数初始化应用,并设置窗口和各个UI组件。
func main() {
a := app.New()
w := a.NewWindow("API工具")
w.Resize(fyne.NewSize(600, 600))
// 创建输入框和输出框
apiURL := widget.NewEntry()
apiURL.SetPlaceHolder("请输入API URL")
apiKey := widget.NewEntry()
apiKey.SetPlaceHolder("请输入您的API Key")
output := widget.NewMultiLineEntry()
output.SetPlaceHolder("输出将在这里展示...\n")
output.SetMinRowsVisible(15)
// 创建按钮
getBalance := widget.NewButton("获取余额", func() {
handleGetBalance(apiURL.Text, apiKey.Text, output, w)
})
getModels := widget.NewButton("获取模型列表", func() {
handleGetModels(apiURL.Text, apiKey.Text, output, w)
})
testModel := widget.NewButton("测试模型", func() {
showTestModelDialog(apiURL.Text, apiKey.Text, output, w)
})
// 布局组件
layout := container.NewVBox(
widget.NewLabel("API URL:"),
apiURL,
widget.NewLabel("API Key:"),
apiKey,
getBalance,
getModels,
testModel,
container.NewMax(output),
)
w.SetContent(layout)
w.ShowAndRun()
}
输入验证
为了确保API请求能够成功,我们需要对用户输入的API URL和API Key进行验证。
func validateInputs(apiURL, apiKey string) error {
if apiURL == "" || apiKey == "" {
return fmt.Errorf("请填写API URL和API Key")
}
return nil
}
获取余额
该功能涉及两个API请求:一个用于获取订阅信息,另一个用于获取账单信息。我们将这些内容展示在输出框中。
func handleGetBalance(apiURL, apiKey string, output *widget.Entry, w fyne.Window) {
if err := validateInputs(apiURL, apiKey); err != nil {
dialog.ShowError(err, w)
return
}
// 获取订阅信息
url := fmt.Sprintf("%s/v1/dashboard/billing/subscription", strings.TrimSpace(apiURL))
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var data SubscriptionData
if err := json.Unmarshal(body, &data); err != nil {
dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
return
}
// 获取账单信息
startDate := "2021-01-01"
endDate := "2022-01-01"
billingURL := fmt.Sprintf("%s/v1/dashboard/billing/usage?start_date=%s&end_date=%s", strings.TrimSpace(apiURL), startDate, endDate)
req, _ = http.NewRequest("GET", billingURL, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))
resp, err = client.Do(req)
if err != nil {
dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
return
}
defer resp.Body.Close()
billingBody, _ := ioutil.ReadAll(resp.Body)
var billingData BillingData
if err := json.Unmarshal(billingBody, &billingData); err != nil {
dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
return
}
remaining := data.HardLimitUSD - billingData.TotalUsage/100
text := fmt.Sprintf("总额: %.4f USD\n已用: %.4f USD\n剩余: %.4f USD", data.HardLimitUSD, billingData.TotalUsage/100, remaining)
output.SetText(text)
}
获取模型列表
该功能通过API请求获取模型列表,并将结果展示在输出框中。
func handleGetModels(apiURL, apiKey string, output *widget.Entry, w fyne.Window) {
if err := validateInputs(apiURL, apiKey); err != nil {
dialog.ShowError(err, w)
return
}
url := fmt.Sprintf("%s/v1/models", strings.TrimSpace(apiURL))
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var modelData ModelData
if err := json.Unmarshal(body, &modelData); err != nil {
dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
return
}
models := make([]string, len(modelData.Data))
for i, model := range modelData.Data {
models[i] = model.ID
}
text := fmt.Sprintf("模型列表:\n%s", strings.Join(models, "\n"))
output.SetText(text)
}
测试模型
该功能展示了一个弹出的对话框,允许用户输入模型名称和选择是否返回完整的响应信息。
func showTestModelDialog(apiURL, apiKey string, output *widget.Entry, w fyne.Window) {
modelNameEntry := widget.NewEntry()
modelNameEntry.SetPlaceHolder("模型名称 (gpt-3.5-turbo)")
fullResponseCheckbox := widget.NewCheck("返回完整信息", nil)
var modal *widget.PopUp
submitButton := widget.NewButton("提交", func() {
modelName := modelNameEntry.Text
if modelName == "" {
modelName = "gpt-3.5-turbo"
}
testModelRequest(apiURL, apiKey, modelName, fullResponseCheckbox.Checked, output, w)
if modal != nil {
modal.Hide()
}
})
content := container.NewVBox(
widget.NewLabel("请输入模型名称 (默认使用 gpt-3.5-turbo):"),
modelNameEntry,
fullResponseCheckbox,
)
closeButton := widget.NewButton("关闭", func() {
if modal != nil {
modal.Hide()
}
})
content.Add(container.NewHBox(submitButton, closeButton))
modal = widget.NewModalPopUp(content, w.Canvas())
modal.Resize(fyne.NewSize(300, 200))
modal.Show()
}
func testModelRequest(apiURL, apiKey, modelName string, fullResponse bool, output *widget.Entry, w fyne.Window) {
if err := validateInputs(apiURL, apiKey); err != nil {
dialog.ShowError(err, w)
return
}
url := fmt.Sprintf("%s/v1/chat/completions", strings.TrimSpace(apiURL))
data := map[string]interface{}{
"model": modelName,
"messages": []map[string]string{
{"role": "user", "content": "say this is a test!"},
},
}
jsonData, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", url, strings.NewReader(string(jsonData)))
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", strings.TrimSpace(apiKey)))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
dialog.ShowError(fmt.Errorf("请求失败: %v", err), w)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var completionResponse CompletionResponse
if err := json.Unmarshal(body, &completionResponse); err != nil {
dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)
return
}
if fullResponse {
var prettyJSON bytes.Buffer
error := json.Indent(&prettyJSON, body, "", " ")
if error != nil {
output.SetText(fmt.Sprintf("格式化JSON失败: %v", error))
} else {
output.SetText(prettyJSON.String())
}
} else if len(completionResponse.Choices) > 0 {
output.SetText(completionResponse.Choices[0].Message.Content)
} else {
output.SetText("未收到模型回应")
}
}
技术要点
1. GUI组件的使用
Fyne 提供了一系列丰富的控件,比如 Entry
, Button
和 MultiLineEntry
。通过配置这些控件,我们可以迅速搭建一个简单且实用的GUI界面。
2. 网络请求与JSON解析
利用 net/http
包来处理HTTP请求。通过自定义请求头和API地址,实现与服务端的数据交互。encoding/json
包用于解析和生成JSON数据,确保了数据格式的一致性。
3. 错误处理与用户反馈
采用对话框(dialog
)来显示错误信息,增强了用户体验。当请求失败或JSON解析出错时,能够及时反馈给用户,减少用户的疑惑。
4. 弹出对话框与动态内容
使用 widget.NewModalPopUp
创建弹出对话框,使得应用更加灵活。用户可以输入具体的模型名称并选择是否需要完整的响应信息,这种动态交互增强了程序的自适应能力。
运行与调试
- 在你的Go项目目录中创建一个
main.go
文件,并将前文的代码粘贴进去。 - 确保你已经安装了Fyne框架。
- 运行你的程序:
go run main.go
- 按照提示输入API URL和API Key,然后点击各个按钮进行相应操作。
结尾
完整代码地址:https://github.com/stfghly/test-all-api/blob/main/test.go
通过这篇博客,我们了解了如何使用Go语言和Fyne构建一个简易的API工具。这不仅展示了Go语言强大的并发和网络处理能力,同时也展示了Fyne框架在构建桌面应用时的便利性。希望这篇博客对你有所帮助,如果你有任何问题或建议,欢迎在评论区交流讨论。