前言
在这篇博客中,我们将介绍如何使用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 SubscriptionDataif 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 BillingDataif err := json.Unmarshal(billingBody, &billingData); err != nil {dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)return}remaining := data.HardLimitUSD - billingData.TotalUsage/100text := 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 ModelDataif 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.PopUpsubmitButton := widget.NewButton("提交", func() {modelName := modelNameEntry.Textif 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 CompletionResponseif err := json.Unmarshal(body, &completionResponse); err != nil {dialog.ShowError(fmt.Errorf("解析响应失败: %v", err), w)return}if fullResponse {var prettyJSON bytes.Buffererror := 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框架在构建桌面应用时的便利性。希望这篇博客对你有所帮助,如果你有任何问题或建议,欢迎在评论区交流讨论。