beego/client/httplib/httpclient.go
2021-04-20 17:54:07 +08:00

297 lines
7.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}