From babc69e13d9ff3a89003d63254578ef0e0d9bd9c Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 15 Jun 2022 16:28:17 +0800 Subject: [PATCH] add DoRequestObj function for gclient.Client --- net/gclient/gclient.go | 16 +++-- net/gclient/gclient_chain.go | 3 + net/gclient/gclient_request_obj.go | 95 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 net/gclient/gclient_request_obj.go diff --git a/net/gclient/gclient.go b/net/gclient/gclient.go index 9219fa3b08e..70450822ce2 100644 --- a/net/gclient/gclient.go +++ b/net/gclient/gclient.go @@ -79,13 +79,17 @@ func New() *Client { func (c *Client) Clone() *Client { newClient := New() *newClient = *c - newClient.header = make(map[string]string) - newClient.cookies = make(map[string]string) - for k, v := range c.header { - newClient.header[k] = v + if len(c.header) > 0 { + newClient.header = make(map[string]string) + for k, v := range c.header { + newClient.header[k] = v + } } - for k, v := range c.cookies { - newClient.cookies[k] = v + if len(c.cookies) > 0 { + newClient.cookies = make(map[string]string) + for k, v := range c.cookies { + newClient.cookies[k] = v + } } return newClient } diff --git a/net/gclient/gclient_chain.go b/net/gclient/gclient_chain.go index a24e2c3dbce..03b046936f8 100644 --- a/net/gclient/gclient_chain.go +++ b/net/gclient/gclient_chain.go @@ -12,6 +12,9 @@ import ( // Prefix is a chaining function, // which sets the URL prefix for next request of this client. +// Eg: +// Prefix("http://127.0.0.1:8199/api/v1") +// Prefix("http://127.0.0.1:8199/api/v2") func (c *Client) Prefix(prefix string) *Client { newClient := c.Clone() newClient.SetPrefix(prefix) diff --git a/net/gclient/gclient_request_obj.go b/net/gclient/gclient_request_obj.go new file mode 100644 index 00000000000..4fa48bd3e4c --- /dev/null +++ b/net/gclient/gclient_request_obj.go @@ -0,0 +1,95 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gclient + +import ( + "context" + "net/http" + "reflect" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/net/goai" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gmeta" +) + +// DoRequestObj does HTTP request using standard request/response object. +// The request object `req` is defined like: +// type UseCreateReq struct { +// g.Meta `path:"/user" method:"put"` +// // other fields.... +// } +// The response object `res` should be a pointer type. It automatically converts result +// to given object `res` is success. +// Eg: +// var ( +// req = UseCreateReq{} +// res *UseCreateRes +// ) +// DoRequestObj(ctx, req, &res) +func (c *Client) DoRequestObj(ctx context.Context, req, res interface{}) error { + var ( + method = gmeta.Get(req, goai.TagNameMethod).String() + path = gmeta.Get(req, goai.TagNamePath).String() + ) + if method == "" { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `no "%s" tag found in request object: %s`, + goai.TagNameMethod, reflect.TypeOf(req).String(), + ) + } + if path == "" { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `no "%s" tag found in request object: %s`, + goai.TagNamePath, reflect.TypeOf(req).String(), + ) + } + path = c.handlePathForObjRequest(path, req) + switch gstr.ToUpper(method) { + case + http.MethodGet, + http.MethodPut, + http.MethodPost, + http.MethodDelete, + http.MethodHead, + http.MethodPatch, + http.MethodConnect, + http.MethodOptions, + http.MethodTrace: + if result := c.RequestVar(ctx, method, path, req); res != nil && !result.IsEmpty() { + return result.Scan(res) + } + return nil + + default: + return gerror.Newf(`invalid HTTP method "%s"`, method) + } +} + +// handlePathForObjRequest replaces parameters in `path` with parameters from request object. +// Eg: +// /order/{id} -> /order/1 +// /user/{name} -> /order/john +func (c *Client) handlePathForObjRequest(path string, req interface{}) string { + if gstr.Contains(path, "{") { + requestParamsMap := gconv.MapStrStr(req) + if len(requestParamsMap) > 0 { + path, _ = gregex.ReplaceStringFuncMatch(`\{(\w+)\}`, path, func(match []string) string { + if v, ok := requestParamsMap[match[1]]; ok { + return v + } + return match[1] + }) + } + } + return path +}