174 lines
4.5 KiB
Go
174 lines
4.5 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
|
"resty.dev/v3"
|
|
)
|
|
|
|
// HttpClientService HTTP客户端服务
|
|
type HttpClientService struct {
|
|
logger *log.LogService
|
|
client *resty.Client
|
|
}
|
|
|
|
// HttpRequest HTTP请求结构
|
|
type HttpRequest struct {
|
|
Method string `json:"method"`
|
|
URL string `json:"url"`
|
|
Headers map[string]string `json:"headers"`
|
|
BodyType string `json:"bodyType,omitempty"` // json, formdata, urlencoded, text
|
|
Body interface{} `json:"body,omitempty"`
|
|
}
|
|
|
|
// HttpResponse HTTP响应结构
|
|
type HttpResponse struct {
|
|
Status string `json:"status"` // 使用resp.Status()返回完整状态如"200 OK"
|
|
Time int64 `json:"time"` // 响应时间(毫秒)
|
|
RequestSize string `json:"requestSize"` // 请求大小
|
|
Body interface{} `json:"body"`
|
|
Headers http.Header `json:"headers"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Error interface{} `json:"error,omitempty"`
|
|
}
|
|
|
|
// NewHttpClientService 创建新的HTTP客户端服务实例
|
|
func NewHttpClientService(logger *log.LogService) *HttpClientService {
|
|
client := resty.New().
|
|
SetTimeout(30 * time.Second).
|
|
SetRetryCount(0).
|
|
EnableTrace().
|
|
SetHeaders(map[string]string{
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Methods": "*",
|
|
"Access-Control-Allow-Headers": "*",
|
|
"Access-Control-Allow-Credentials": "true",
|
|
})
|
|
|
|
return &HttpClientService{
|
|
logger: logger,
|
|
client: client,
|
|
}
|
|
}
|
|
|
|
// ExecuteRequest 执行HTTP请求
|
|
func (hcs *HttpClientService) ExecuteRequest(ctx context.Context, request *HttpRequest) (*HttpResponse, error) {
|
|
// 创建请求
|
|
req := hcs.client.R().SetContext(ctx)
|
|
|
|
// 设置请求头
|
|
if request.Headers != nil {
|
|
req.SetHeaders(request.Headers)
|
|
}
|
|
|
|
// 设置请求体
|
|
if err := hcs.setRequestBody(req, request); err != nil {
|
|
return nil, fmt.Errorf("set request body failed: %w", err)
|
|
}
|
|
|
|
// 执行请求
|
|
resp, err := req.Execute(strings.ToUpper(request.Method), request.URL)
|
|
|
|
// 构建响应对象
|
|
return hcs.buildResponse(resp, err)
|
|
}
|
|
|
|
// setRequestBody 设置请求体
|
|
func (hcs *HttpClientService) setRequestBody(req *resty.Request, request *HttpRequest) error {
|
|
if request.Body == nil {
|
|
return nil
|
|
}
|
|
|
|
switch strings.ToLower(request.BodyType) {
|
|
case "json":
|
|
req.SetHeader("Content-Type", "application/json")
|
|
req.SetBody(request.Body)
|
|
case "formdata":
|
|
if formData, ok := request.Body.(map[string]interface{}); ok {
|
|
for key, value := range formData {
|
|
valueStr := fmt.Sprintf("%v", value)
|
|
// 检查是否是文件类型,使用 @file 关键词
|
|
if strings.HasPrefix(valueStr, "@file ") {
|
|
// 提取文件路径(去掉 @file 前缀)
|
|
filePath := strings.TrimSpace(strings.TrimPrefix(valueStr, "@file "))
|
|
req.SetFile(key, filePath)
|
|
} else {
|
|
// 普通表单字段
|
|
req.SetFormData(map[string]string{key: valueStr})
|
|
}
|
|
}
|
|
}
|
|
case "urlencoded":
|
|
req.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
|
if formData, ok := request.Body.(map[string]interface{}); ok {
|
|
values := url.Values{}
|
|
for key, value := range formData {
|
|
values.Set(key, fmt.Sprintf("%v", value))
|
|
}
|
|
req.SetBody(values.Encode())
|
|
}
|
|
case "text":
|
|
req.SetHeader("Content-Type", "text/plain")
|
|
req.SetBody(fmt.Sprintf("%v", request.Body))
|
|
default:
|
|
return fmt.Errorf("unsupported body type: %s", request.BodyType)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// buildResponse 构建响应对象
|
|
func (hcs *HttpClientService) buildResponse(resp *resty.Response, err error) (*HttpResponse, error) {
|
|
httpResp := &HttpResponse{
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
// 如果有错误,设置错误状态并返回
|
|
if err != nil {
|
|
httpResp.Status = "Request Failed"
|
|
httpResp.Error = err.Error()
|
|
return httpResp, nil
|
|
}
|
|
|
|
// 设置基本响应信息
|
|
httpResp.Status = resp.Status()
|
|
httpResp.Time = resp.Duration().Milliseconds()
|
|
httpResp.RequestSize = hcs.formatBytes(resp.Size())
|
|
|
|
if errorData := resp.Error(); errorData != nil {
|
|
httpResp.Error = errorData
|
|
}
|
|
|
|
// 设置响应头
|
|
httpResp.Headers = resp.Header()
|
|
httpResp.Body = resp.String()
|
|
|
|
return httpResp, nil
|
|
}
|
|
|
|
// formatBytes 格式化字节大小
|
|
func (hcs *HttpClientService) formatBytes(bytes int64) string {
|
|
if bytes < 0 {
|
|
return "0 B"
|
|
}
|
|
|
|
const unit = 1024
|
|
if bytes < unit {
|
|
return fmt.Sprintf("%d B", bytes)
|
|
}
|
|
|
|
div, exp := int64(unit), 0
|
|
for n := bytes / unit; n >= unit; n /= unit {
|
|
div *= unit
|
|
exp++
|
|
}
|
|
|
|
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
|
}
|