generic.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package generic
  2. import (
  3. context2 "context"
  4. "net/url"
  5. "strings"
  6. "github.com/silenceper/wechat/v2/miniprogram/context"
  7. "github.com/silenceper/wechat/v2/util"
  8. )
  9. // defaultBaseURL 微信 API 默认基础地址
  10. const defaultBaseURL = "https://api.weixin.qq.com"
  11. // Generic 通用 API 调用,用于访问 SDK 尚未封装的小程序接口。
  12. // 支持自定义 path、query 参数、request body 以及 response 结构体。
  13. type Generic struct {
  14. *context.Context
  15. }
  16. // NewGeneric init
  17. func NewGeneric(ctx *context.Context) *Generic {
  18. return &Generic{ctx}
  19. }
  20. // BuildURL 构建完整请求 URL。
  21. // path 支持相对路径(如 "/intp/xxx")或完整 URL(如 "https://api.weixin.qq.com/intp/xxx")。
  22. // query 为额外的 URL 查询参数,可为 nil;access_token 会自动追加(若 query 中已包含则不重复追加)。
  23. func (g *Generic) BuildURL(path string, query url.Values) (string, error) {
  24. return g.BuildURLContext(context2.Background(), path, query)
  25. }
  26. // BuildURLContext 构建完整请求 URL(带 context)
  27. func (g *Generic) BuildURLContext(ctx context2.Context, path string, query url.Values) (string, error) {
  28. var baseURL string
  29. switch {
  30. case strings.HasPrefix(path, "http://"), strings.HasPrefix(path, "https://"):
  31. baseURL = path
  32. case strings.HasPrefix(path, "/"):
  33. baseURL = defaultBaseURL + path
  34. default:
  35. baseURL = defaultBaseURL + "/" + path
  36. }
  37. // 克隆 query,避免修改调用方原始值
  38. q := make(url.Values, len(query)+1)
  39. for k, v := range query {
  40. q[k] = v
  41. }
  42. // 自动追加 access_token
  43. if q.Get("access_token") == "" {
  44. accessToken, err := g.GetAccessTokenContext(ctx)
  45. if err != nil {
  46. return "", err
  47. }
  48. q.Set("access_token", accessToken)
  49. }
  50. sep := "?"
  51. if strings.Contains(baseURL, "?") {
  52. sep = "&"
  53. }
  54. return baseURL + sep + q.Encode(), nil
  55. }
  56. // Get 发送 GET 请求,返回原始响应字节。
  57. // 使用 util.DecodeWithError 可将响应解码到自定义结构体。
  58. func (g *Generic) Get(path string, query url.Values) (response []byte, err error) {
  59. return g.GetContext(context2.Background(), path, query)
  60. }
  61. // GetContext 发送 GET 请求(带 context),返回原始响应字节
  62. func (g *Generic) GetContext(ctx context2.Context, path string, query url.Values) (response []byte, err error) {
  63. var uri string
  64. if uri, err = g.BuildURLContext(ctx, path, query); err != nil {
  65. return
  66. }
  67. return util.HTTPGetContext(ctx, uri)
  68. }
  69. // PostJSON 发送 POST 请求(JSON body),返回原始响应字节。
  70. // body 可为 nil(无请求体);使用 util.DecodeWithError 可将响应解码到自定义结构体。
  71. func (g *Generic) PostJSON(path string, query url.Values, body interface{}) (response []byte, err error) {
  72. return g.PostJSONContext(context2.Background(), path, query, body)
  73. }
  74. // PostJSONContext 发送 POST 请求(JSON body,带 context),返回原始响应字节
  75. func (g *Generic) PostJSONContext(ctx context2.Context, path string, query url.Values, body interface{}) (response []byte, err error) {
  76. var uri string
  77. if uri, err = g.BuildURLContext(ctx, path, query); err != nil {
  78. return
  79. }
  80. return util.PostJSONContext(ctx, uri, body)
  81. }
  82. // GetDecode 发送 GET 请求并将响应解码到 resp。
  83. // resp 应为指向结构体的指针,结构体中可嵌入 util.CommonError 以自动校验错误码。
  84. // apiName 用于错误信息标识。
  85. func (g *Generic) GetDecode(path string, query url.Values, resp interface{}, apiName string) (err error) {
  86. return g.GetDecodeContext(context2.Background(), path, query, resp, apiName)
  87. }
  88. // GetDecodeContext 发送 GET 请求并将响应解码到 resp(带 context)
  89. func (g *Generic) GetDecodeContext(ctx context2.Context, path string, query url.Values, resp interface{}, apiName string) (err error) {
  90. var response []byte
  91. if response, err = g.GetContext(ctx, path, query); err != nil {
  92. return
  93. }
  94. return util.DecodeWithError(response, resp, apiName)
  95. }
  96. // PostJSONDecode 发送 POST 请求(JSON body)并将响应解码到 resp。
  97. // resp 应为指向结构体的指针,结构体中可嵌入 util.CommonError 以自动校验错误码。
  98. // apiName 用于错误信息标识。
  99. func (g *Generic) PostJSONDecode(path string, query url.Values, body, resp interface{}, apiName string) (err error) {
  100. return g.PostJSONDecodeContext(context2.Background(), path, query, body, resp, apiName)
  101. }
  102. // PostJSONDecodeContext 发送 POST 请求(JSON body)并将响应解码到 resp(带 context)
  103. func (g *Generic) PostJSONDecodeContext(ctx context2.Context, path string, query url.Values, body, resp interface{}, apiName string) (err error) {
  104. var response []byte
  105. if response, err = g.PostJSONContext(ctx, path, query, body); err != nil {
  106. return
  107. }
  108. return util.DecodeWithError(response, resp, apiName)
  109. }