-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: initialize client && request #1
base: main
Are you sure you want to change the base?
Changes from 12 commits
1cf83b3
61c4c00
5641152
f7ac4a6
3c45281
7f12c8e
4fa2fce
59839bc
535fbc4
9f0b5ab
cc62cee
ac162ef
52406c3
da9bb18
ec738a7
7ce34d5
ae38876
df441ff
00d9903
f7fc302
1c43340
b1be890
6ec475e
86ddbe1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,302 @@ | ||
/* | ||
* Copyright 2024 CloudWeGo Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package easy_http | ||
|
||
import ( | ||
"context" | ||
"github.com/cloudwego/hertz/pkg/app/client" | ||
"github.com/cloudwego/hertz/pkg/protocol" | ||
"net/http" | ||
"net/url" | ||
"regexp" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
type Client struct { | ||
QueryParam url.Values | ||
FormData map[string]string | ||
PathParams map[string]string | ||
Header http.Header | ||
Cookies []*http.Cookie | ||
|
||
beforeRequest []RequestMiddleware | ||
udBeforeRequest []RequestMiddleware | ||
afterResponse []ResponseMiddleware | ||
afterResponseLock *sync.RWMutex | ||
udBeforeRequestLock *sync.RWMutex | ||
|
||
client *client.Client | ||
} | ||
|
||
type ( | ||
RequestMiddleware func(*Client, *Request) error | ||
ResponseMiddleware func(*Client, *Response) error | ||
) | ||
|
||
var ( | ||
hdrUserAgentKey = http.CanonicalHeaderKey("User-Agent") | ||
hdrAcceptKey = http.CanonicalHeaderKey("Accept") | ||
hdrContentTypeKey = http.CanonicalHeaderKey("Content-Type") | ||
hdrContentLengthKey = http.CanonicalHeaderKey("Content-Length") | ||
hdrContentEncodingKey = http.CanonicalHeaderKey("Content-Encoding") | ||
hdrLocationKey = http.CanonicalHeaderKey("Location") | ||
hdrAuthorizationKey = http.CanonicalHeaderKey("Authorization") | ||
hdrWwwAuthenticateKey = http.CanonicalHeaderKey("WWW-Authenticate") | ||
|
||
plainTextType = "text/plain; charset=utf-8" | ||
jsonContentType = "application/json" | ||
formContentType = "application/x-www-form-urlencoded" | ||
formDataContentType = "multipart/form-data" | ||
|
||
jsonCheck = regexp.MustCompile(`(?i:(application|text)/(.*json.*)(;|$))`) | ||
xmlCheck = regexp.MustCompile(`(?i:(application|text)/(.*xml.*)(;|$))`) | ||
) | ||
|
||
func createClient(cc *client.Client) *Client { | ||
c := &Client{ | ||
QueryParam: url.Values{}, | ||
PathParams: make(map[string]string), | ||
Header: http.Header{}, | ||
Cookies: make([]*http.Cookie, 0), | ||
|
||
udBeforeRequestLock: &sync.RWMutex{}, | ||
afterResponseLock: &sync.RWMutex{}, | ||
|
||
client: cc, | ||
} | ||
|
||
c.beforeRequest = []RequestMiddleware{ | ||
parseRequestURL, | ||
parseRequestHeader, | ||
parseRequestBody, | ||
} | ||
|
||
c.udBeforeRequest = []RequestMiddleware{} | ||
|
||
c.afterResponse = []ResponseMiddleware{} | ||
|
||
return c | ||
} | ||
|
||
func (c *Client) SetQueryParam(param, value string) *Client { | ||
c.QueryParam.Set(param, value) | ||
return c | ||
} | ||
|
||
func (c *Client) SetQueryParams(params map[string]string) *Client { | ||
for k, v := range params { | ||
c.QueryParam.Set(k, v) | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) SetQueryParamsFromValues(params url.Values) *Client { | ||
for k, v := range params { | ||
for _, v1 := range v { | ||
c.QueryParam.Add(k, v1) | ||
} | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) SetQueryString(query string) *Client { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 没实现的地方可以记个 todo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SetQueryString 可以给个示例用法吗 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SetQueryString() 是 hertz Request 的原始 API,可以直接转换一下就行https://github.com/cloudwego/hertz/blob/develop/pkg/protocol/request.go#L457 |
||
str := strings.Split(query, "&") | ||
for _, v := range str { | ||
kv := strings.Split(v, "=") | ||
if len(kv) == 2 { | ||
c.QueryParam.Set(kv[0], kv[1]) | ||
} | ||
|
||
} | ||
return c | ||
} | ||
|
||
func (c *Client) AddQueryParam(param, value string) *Client { | ||
c.QueryParam.Add(param, value) | ||
return c | ||
} | ||
|
||
func (c *Client) AddQueryParams(params map[string]string) *Client { | ||
for k, v := range params { | ||
c.QueryParam.Add(k, v) | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) SetPathParam(param, value string) *Client { | ||
c.PathParams[param] = value | ||
return c | ||
} | ||
|
||
func (c *Client) SetPathParams(params map[string]string) *Client { | ||
for k, v := range params { | ||
c.PathParams[k] = v | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) SetHeader(header, value string) *Client { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有些 header 有 k-[]v的形式,包括上面的query,可以考虑支持下这种数组类型 |
||
c.Header.Set(header, value) | ||
return c | ||
} | ||
|
||
func (c *Client) SetHeaders(headers map[string]string) *Client { | ||
for k, v := range headers { | ||
c.Header.Set(k, v) | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) SetHeaderMultiValues(headers map[string][]string) *Client { | ||
for k, header := range headers { | ||
for _, v := range header { | ||
c.Header.Add(k, v) | ||
} | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) AddHeader(header, value string) *Client { | ||
c.Header.Add(header, value) | ||
return c | ||
} | ||
|
||
func (c *Client) AddHeaders(headers map[string]string) *Client { | ||
for k, v := range headers { | ||
c.Header.Add(k, v) | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) AddHeaderMultiValues(headers map[string][]string) *Client { | ||
for k, header := range headers { | ||
for _, v := range header { | ||
c.Header.Add(k, v) | ||
} | ||
} | ||
return c | ||
} | ||
|
||
func (c *Client) SetContentType(contentType string) *Client { | ||
c.Header.Set("Content-Type", contentType) | ||
return c | ||
} | ||
|
||
func (c *Client) SetJSONContentType() *Client { | ||
c.Header.Set("Content-Type", "application/json") | ||
return c | ||
} | ||
|
||
func (c *Client) SetXMLContentType() *Client { | ||
c.Header.Set("Content-Type", "application/xml") | ||
return c | ||
} | ||
|
||
func (c *Client) SetHTMLContentType() *Client { | ||
c.Header.Set("Content-Type", "text/html") | ||
return c | ||
} | ||
|
||
func (c *Client) SetFormContentType() *Client { | ||
c.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
return c | ||
|
||
} | ||
|
||
func (c *Client) SetXFormData() *Client { | ||
FGYFFFF marked this conversation as resolved.
Show resolved
Hide resolved
|
||
c.Header.Set("Content-Type", "multipart/form-data") | ||
return c | ||
} | ||
|
||
func (c *Client) SetCookie(hc *http.Cookie) *Client { | ||
c.Cookies = append(c.Cookies, hc) | ||
return c | ||
} | ||
|
||
func (c *Client) SetCookies(hcs []*http.Cookie) *Client { | ||
c.Cookies = append(c.Cookies, hcs...) | ||
return c | ||
} | ||
|
||
func (c *Client) R() *Request { | ||
r := &Request{ | ||
QueryParam: url.Values{}, | ||
Header: http.Header{}, | ||
PathParams: map[string]string{}, | ||
RawRequest: &protocol.Request{}, | ||
|
||
client: c, | ||
} | ||
return r | ||
} | ||
|
||
func (c *Client) NewRequest() *Request { | ||
return c.R() | ||
} | ||
|
||
func (c *Client) execute(req *Request) (*Response, error) { | ||
// Lock the user-defined pre-request hooks. | ||
c.udBeforeRequestLock.RLock() | ||
defer c.udBeforeRequestLock.RUnlock() | ||
|
||
// Lock the post-request hooks. | ||
c.afterResponseLock.RLock() | ||
defer c.afterResponseLock.RUnlock() | ||
|
||
// Apply Request middleware | ||
var err error | ||
|
||
// user defined on before request methods | ||
// to modify the *resty.Request object | ||
for _, f := range c.udBeforeRequest { | ||
if err = f(c, req); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
for _, f := range c.beforeRequest { | ||
if err = f(c, req); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
if hostHeader := req.Header.Get("Host"); hostHeader != "" { | ||
req.RawRequest.SetHost(hostHeader) | ||
} | ||
|
||
resp := &protocol.Response{} | ||
err = c.client.Do(context.Background(), req.RawRequest, resp) | ||
|
||
response := &Response{ | ||
Request: req, | ||
RawResponse: resp, | ||
} | ||
|
||
if err != nil { | ||
return response, err | ||
} | ||
|
||
// Apply Response middleware | ||
for _, f := range c.afterResponse { | ||
if err = f(c, response); err != nil { | ||
break | ||
} | ||
} | ||
|
||
return response, err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package easy_http | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func TestSetQueryParam(t *testing.T) { | ||
c := MustNew() | ||
c.SetQueryParam("test1", "test1") | ||
c.SetQueryParams(map[string]string{"test2": "test2", "test3": "test3"}) | ||
c.SetQueryParamsFromValues(map[string][]string{"test4": {"test41", "test42"}}) | ||
c.SetQueryString("test5=test5") | ||
|
||
assert.Equal(t, "test1", c.QueryParam.Get("test1")) | ||
assert.Equal(t, "test2", c.QueryParam.Get("test2")) | ||
assert.Equal(t, "test3", c.QueryParam.Get("test3")) | ||
assert.Equal(t, []string{"test41", "test42"}, c.QueryParam["test4"]) | ||
assert.Equal(t, "test5", c.QueryParam.Get("test5")) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Copyright 2024 CloudWeGo Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package easy_http | ||
|
||
import "github.com/cloudwego/hertz/pkg/app/client" | ||
|
||
func New() (*Client, error) { | ||
c, err := client.NewClient() | ||
return createClient(c), err | ||
} | ||
|
||
func MustNew() *Client { | ||
c, err := client.NewClient() | ||
if err != nil { | ||
panic(err) | ||
} | ||
return createClient(c) | ||
} | ||
|
||
func NewWithHertzClient(c *client.Client) *Client { | ||
return createClient(c) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package easy_http |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hertz-contrib/easy_http" | ||
) | ||
|
||
func main() { | ||
c := easy_http.MustNew() | ||
|
||
res, err := c.SetHeader("test", "test").SetQueryParam("test1", "test1").R().Get("http://www.baidu.com") | ||
if err != nil { | ||
panic(err) | ||
} | ||
fmt.Println(res) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
middleware 可以不用支持, hertz client 本身就有 mw 能力,可以把 hertz client mw 以配置的形式注入,就别在 封装的这一层再搞一层 Middleware 了
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
看了下面这个 Middleware 是在用来创建 hertz 的 Request;这块进行不要对外暴露就好,保证easy_http 内部可用就好