add httpclient add options

This commit is contained in:
holooooo
2021-04-20 17:52:33 +08:00
parent 29849ddb36
commit 575bf62fd3
8 changed files with 716 additions and 80 deletions

View File

@@ -0,0 +1,296 @@
// Copyright 2020 beego
//
// 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 httplib
import (
"net/http"
"strings"
"github.com/beego/beego/v2/core/berror"
)
// Client provides an HTTP client supporting chain call
type Client struct {
Name string
Endpoint string
CommonOpts []BeegoHttpRequestOption
Setting *BeegoHTTPSettings
pointer *responsePointer
}
type responsePointer struct {
response **http.Response
statusCode **int
header **http.Header
headerValues map[string]**string //用户传一个key然后将key存在map的key里header的value存在value里
contentLength **int64
}
// NewClient return a new http client
func NewClient(name string, endpoint string, opts ...ClientOption) (*Client, error) {
res := &Client{
Name: name,
Endpoint: endpoint,
}
setting := GetDefaultSetting()
res.Setting = &setting
for _, o := range opts {
err := o(res)
if err != nil {
return nil, err
}
}
return res, nil
}
// Response will set response to the pointer
func (c *Client) Response(resp **http.Response) *Client {
if c.pointer == nil {
newC := *c
newC.pointer = &responsePointer{
response: resp,
}
return &newC
}
c.pointer.response = resp
return c
}
// StatusCode will set response StatusCode to the pointer
func (c *Client) StatusCode(code **int) *Client {
if c.pointer == nil {
newC := *c
newC.pointer = &responsePointer{
statusCode: code,
}
return &newC
}
c.pointer.statusCode = code
return c
}
// Headers will set response Headers to the pointer
func (c *Client) Headers(headers **http.Header) *Client {
if c.pointer == nil {
newC := *c
newC.pointer = &responsePointer{
header: headers,
}
return &newC
}
c.pointer.header = headers
return c
}
// HeaderValue will set response HeaderValue to the pointer
func (c *Client) HeaderValue(key string, value **string) *Client {
if c.pointer == nil {
newC := *c
newC.pointer = &responsePointer{
headerValues: map[string]**string{
key: value,
},
}
return &newC
}
if c.pointer.headerValues == nil {
c.pointer.headerValues = map[string]**string{}
}
c.pointer.headerValues[key] = value
return c
}
// ContentType will set response ContentType to the pointer
func (c *Client) ContentType(contentType **string) *Client {
return c.HeaderValue("Content-Type", contentType)
}
// ContentLength will set response ContentLength to the pointer
func (c *Client) ContentLength(contentLength **int64) *Client {
if c.pointer == nil {
newC := *c
newC.pointer = &responsePointer{
contentLength: contentLength,
}
return &newC
}
c.pointer.contentLength = contentLength
return c
}
// setPointers set the http response value to pointer
func (c *Client) setPointers(resp *http.Response) {
if c.pointer == nil {
return
}
if c.pointer.response != nil {
*c.pointer.response = resp
}
if c.pointer.statusCode != nil {
*c.pointer.statusCode = &resp.StatusCode
}
if c.pointer.header != nil {
*c.pointer.header = &resp.Header
}
if c.pointer.headerValues != nil {
for k, v := range c.pointer.headerValues {
s := resp.Header.Get(k)
*v = &s
}
}
if c.pointer.contentLength != nil {
*c.pointer.contentLength = &resp.ContentLength
}
}
// initRequest will apply all the client setting, common option and request option
func (c *Client) newRequest(method, path string, opts []BeegoHttpRequestOption) (*BeegoHTTPRequest, error) {
var req *BeegoHTTPRequest
switch method {
case http.MethodGet:
req = Get(c.Endpoint + path)
case http.MethodPost:
req = Post(c.Endpoint + path)
case http.MethodPut:
req = Put(c.Endpoint + path)
case http.MethodDelete:
req = Delete(c.Endpoint + path)
case http.MethodHead:
req = Head(c.Endpoint + path)
}
req = req.Setting(*c.Setting)
for _, o := range c.CommonOpts {
err := o(req)
if err != nil {
return nil, err
}
}
for _, o := range opts {
err := o(req)
if err != nil {
return nil, err
}
}
return req, nil
}
// handleResponse try to parse body to meaningful value
func (c *Client) handleResponse(value interface{}, req *BeegoHTTPRequest) error {
// send request
resp, err := req.Response()
if err != nil {
return err
}
c.setPointers(resp)
if value == nil {
return nil
}
// handle basic type
switch v := value.(type) {
case **string:
s, err := req.String()
if err != nil {
return nil
}
*v = &s
return nil
case **[]byte:
bs, err := req.Bytes()
if err != nil {
return nil
}
*v = &bs
return nil
}
// try to parse it as content type
switch strings.Split(resp.Header.Get("Content-Type"), ";")[0] {
case "application/json":
return req.ToJSON(value)
case "text/xml", "application/xml":
return req.ToXML(value)
case "text/yaml", "application/x-yaml":
return req.ToYAML(value)
}
// try to parse it anyway
if err := req.ToJSON(value); err == nil {
return nil
}
if err := req.ToYAML(value); err == nil {
return nil
}
if err := req.ToXML(value); err == nil {
return nil
}
// TODO add new error type about can't parse body
return berror.Error(UnsupportedBodyType, "unsupported body data")
}
// Get Send a GET request and try to give its result value
func (c *Client) Get(value interface{}, path string, opts ...BeegoHttpRequestOption) error {
req, err := c.newRequest(http.MethodGet, path, opts)
if err != nil {
return err
}
return c.handleResponse(value, req)
}
// Post Send a POST request and try to give its result value
func (c *Client) Post(value interface{}, path string, body interface{}, opts ...BeegoHttpRequestOption) error {
req, err := c.newRequest(http.MethodPost, path, opts)
if err != nil {
return err
}
if body != nil {
req = req.Body(body)
}
return c.handleResponse(value, req)
}
// Put Send a Put request and try to give its result value
func (c *Client) Put(value interface{}, path string, body interface{}, opts ...BeegoHttpRequestOption) error {
req, err := c.newRequest(http.MethodPut, path, opts)
if err != nil {
return err
}
if body != nil {
req = req.Body(body)
}
return c.handleResponse(value, req)
}
// Delete Send a Delete request and try to give its result value
func (c *Client) Delete(value interface{}, path string, opts ...BeegoHttpRequestOption) error {
req, err := c.newRequest(http.MethodDelete, path, opts)
if err != nil {
return err
}
return c.handleResponse(value, req)
}
// Head Send a Head request and try to give its result value
func (c *Client) Head(value interface{}, path string, opts ...BeegoHttpRequestOption) error {
req, err := c.newRequest(http.MethodHead, path, opts)
if err != nil {
return err
}
return c.handleResponse(value, req)
}