github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_client_request.go (about)

     1  // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  // HTTP客户端请求.
     8  
     9  package ghttp
    10  
    11  import (
    12  	"bytes"
    13  	"crypto/tls"
    14  	"encoding/json"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"mime/multipart"
    19  	"net/http"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/zhongdalu/gf/g/os/gfile"
    25  )
    26  
    27  // http客户端
    28  type Client struct {
    29  	http.Client                     // 底层http client对象
    30  	header        map[string]string // HEADER信息Map
    31  	cookies       map[string]string // 自定义COOKIE
    32  	prefix        string            // 设置请求的URL前缀
    33  	authUser      string            // HTTP基本权限设置:名称
    34  	authPass      string            // HTTP基本权限设置:密码
    35  	browserMode   bool              // 是否模拟浏览器模式(自动保存提交COOKIE)
    36  	retryCount    int               // 失败重试次数(网络失败情况下)
    37  	retryInterval int               // 失败重试间隔
    38  }
    39  
    40  // http客户端对象指针
    41  func NewClient() *Client {
    42  	return &Client{
    43  		Client: http.Client{
    44  			Transport: &http.Transport{
    45  				// 默认不校验HTTPS证书有效性
    46  				TLSClientConfig: &tls.Config{
    47  					InsecureSkipVerify: true,
    48  				},
    49  				// 默认关闭KeepAlive功能
    50  				DisableKeepAlives: true,
    51  			},
    52  		},
    53  		header:  make(map[string]string),
    54  		cookies: make(map[string]string),
    55  	}
    56  }
    57  
    58  // 克隆当前客户端对象,复制属性。
    59  func (c *Client) Clone() *Client {
    60  	newClient := NewClient()
    61  	*newClient = *c
    62  	newClient.header = make(map[string]string)
    63  	newClient.cookies = make(map[string]string)
    64  	for k, v := range c.header {
    65  		newClient.header[k] = v
    66  	}
    67  	for k, v := range c.cookies {
    68  		newClient.cookies[k] = v
    69  	}
    70  	return newClient
    71  }
    72  
    73  // GET请求
    74  func (c *Client) Get(url string) (*ClientResponse, error) {
    75  	return c.DoRequest("GET", url)
    76  }
    77  
    78  // PUT请求
    79  func (c *Client) Put(url string, data ...interface{}) (*ClientResponse, error) {
    80  	return c.DoRequest("PUT", url, data...)
    81  }
    82  
    83  // POST请求提交数据,默认使用表单方式提交数据(绝大部分场景下也是如此)。
    84  // 如果服务端对Content-Type有要求,可使用Client对象进行请求,单独设置相关属性。
    85  // 支持文件上传,需要字段格式为:FieldName=@file:
    86  func (c *Client) Post(url string, data ...interface{}) (resp *ClientResponse, err error) {
    87  	if len(c.prefix) > 0 {
    88  		url = c.prefix + url
    89  	}
    90  	param := ""
    91  	if len(data) > 0 {
    92  		param = BuildParams(data[0])
    93  	}
    94  	req := (*http.Request)(nil)
    95  	if strings.Contains(param, "@file:") {
    96  		// 文件上传
    97  		buffer := new(bytes.Buffer)
    98  		writer := multipart.NewWriter(buffer)
    99  		for _, item := range strings.Split(param, "&") {
   100  			array := strings.Split(item, "=")
   101  			if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 {
   102  				path := array[1][6:]
   103  				if !gfile.Exists(path) {
   104  					return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
   105  				}
   106  				if file, err := writer.CreateFormFile(array[0], path); err == nil {
   107  					if f, err := os.Open(path); err == nil {
   108  						defer f.Close()
   109  						if _, err = io.Copy(file, f); err != nil {
   110  							return nil, err
   111  						}
   112  					} else {
   113  						return nil, err
   114  					}
   115  				} else {
   116  					return nil, err
   117  				}
   118  			} else {
   119  				writer.WriteField(array[0], array[1])
   120  			}
   121  		}
   122  		writer.Close()
   123  		if req, err = http.NewRequest("POST", url, buffer); err != nil {
   124  			return nil, err
   125  		} else {
   126  			req.Header.Set("Content-Type", writer.FormDataContentType())
   127  		}
   128  	} else {
   129  		// 普通请求
   130  		paramBytes := []byte(param)
   131  		if req, err = http.NewRequest("POST", url, bytes.NewReader(paramBytes)); err != nil {
   132  			return nil, err
   133  		} else {
   134  			if v, ok := c.header["Content-Type"]; ok {
   135  				// 自定义请求类型
   136  				req.Header.Set("Content-Type", v)
   137  			} else {
   138  				// 识别提交数据格式
   139  				if json.Valid(paramBytes) {
   140  					req.Header.Set("Content-Type", "application/json")
   141  				} else {
   142  					req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   143  				}
   144  			}
   145  		}
   146  	}
   147  	// 自定义header
   148  	if len(c.header) > 0 {
   149  		for k, v := range c.header {
   150  			req.Header.Set(k, v)
   151  		}
   152  	}
   153  	// COOKIE
   154  	if len(c.cookies) > 0 {
   155  		headerCookie := ""
   156  		for k, v := range c.cookies {
   157  			if len(headerCookie) > 0 {
   158  				headerCookie += ";"
   159  			}
   160  			headerCookie += k + "=" + v
   161  		}
   162  		if len(headerCookie) > 0 {
   163  			req.Header.Set("Cookie", headerCookie)
   164  		}
   165  	}
   166  	// HTTP账号密码
   167  	if len(c.authUser) > 0 {
   168  		req.SetBasicAuth(c.authUser, c.authPass)
   169  	}
   170  	// 执行请求
   171  	r := (*http.Response)(nil)
   172  	for {
   173  		if r, err = c.Do(req); err != nil {
   174  			if c.retryCount > 0 {
   175  				c.retryCount--
   176  			} else {
   177  				return nil, err
   178  			}
   179  		} else {
   180  			break
   181  		}
   182  	}
   183  	resp = &ClientResponse{
   184  		cookies: make(map[string]string),
   185  	}
   186  	resp.Response = r
   187  	// 浏览器模式
   188  	if c.browserMode {
   189  		now := time.Now()
   190  		for _, v := range r.Cookies() {
   191  			if v.Expires.UnixNano() < now.UnixNano() {
   192  				delete(c.cookies, v.Name)
   193  			} else {
   194  				c.cookies[v.Name] = v.Value
   195  			}
   196  		}
   197  	}
   198  	return resp, nil
   199  }
   200  
   201  // DELETE请求
   202  func (c *Client) Delete(url string, data ...interface{}) (*ClientResponse, error) {
   203  	return c.DoRequest("DELETE", url, data...)
   204  }
   205  
   206  func (c *Client) Head(url string, data ...interface{}) (*ClientResponse, error) {
   207  	return c.DoRequest("HEAD", url, data...)
   208  }
   209  
   210  func (c *Client) Patch(url string, data ...interface{}) (*ClientResponse, error) {
   211  	return c.DoRequest("PATCH", url, data...)
   212  }
   213  
   214  func (c *Client) Connect(url string, data ...interface{}) (*ClientResponse, error) {
   215  	return c.DoRequest("CONNECT", url, data...)
   216  }
   217  
   218  func (c *Client) Options(url string, data ...interface{}) (*ClientResponse, error) {
   219  	return c.DoRequest("OPTIONS", url, data...)
   220  }
   221  
   222  func (c *Client) Trace(url string, data ...interface{}) (*ClientResponse, error) {
   223  	return c.DoRequest("TRACE", url, data...)
   224  }
   225  
   226  // GET请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
   227  func (c *Client) GetContent(url string, data ...interface{}) string {
   228  	return c.DoRequestContent("GET", url, data...)
   229  }
   230  
   231  // PUT请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
   232  func (c *Client) PutContent(url string, data ...interface{}) string {
   233  	return c.DoRequestContent("PUT", url, data...)
   234  }
   235  
   236  // POST请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
   237  func (c *Client) PostContent(url string, data ...interface{}) string {
   238  	return c.DoRequestContent("POST", url, data...)
   239  }
   240  
   241  // DELETE请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
   242  func (c *Client) DeleteContent(url string, data ...interface{}) string {
   243  	return c.DoRequestContent("DELETE", url, data...)
   244  }
   245  
   246  func (c *Client) HeadContent(url string, data ...interface{}) string {
   247  	return c.DoRequestContent("HEAD", url, data...)
   248  }
   249  
   250  func (c *Client) PatchContent(url string, data ...interface{}) string {
   251  	return c.DoRequestContent("PATCH", url, data...)
   252  }
   253  
   254  func (c *Client) ConnectContent(url string, data ...interface{}) string {
   255  	return c.DoRequestContent("CONNECT", url, data...)
   256  }
   257  
   258  func (c *Client) OptionsContent(url string, data ...interface{}) string {
   259  	return c.DoRequestContent("OPTIONS", url, data...)
   260  }
   261  
   262  func (c *Client) TraceContent(url string, data ...interface{}) string {
   263  	return c.DoRequestContent("TRACE", url, data...)
   264  }
   265  
   266  // 请求并返回服务端结果(内部会自动读取服务端返回结果并关闭缓冲区指针)
   267  func (c *Client) DoRequestContent(method string, url string, data ...interface{}) string {
   268  	response, err := c.DoRequest(method, url, data...)
   269  	if err != nil {
   270  		return ""
   271  	}
   272  	defer response.Close()
   273  	return string(response.ReadAll())
   274  }
   275  
   276  // 请求并返回response对象
   277  func (c *Client) DoRequest(method, url string, data ...interface{}) (*ClientResponse, error) {
   278  	if strings.EqualFold("POST", method) {
   279  		return c.Post(url, data...)
   280  	}
   281  	if len(c.prefix) > 0 {
   282  		url = c.prefix + url
   283  	}
   284  	param := ""
   285  	if len(data) > 0 {
   286  		param = BuildParams(data[0])
   287  	}
   288  	req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader([]byte(param)))
   289  	if err != nil {
   290  		return nil, err
   291  	}
   292  	// 自定义header
   293  	if len(c.header) > 0 {
   294  		for k, v := range c.header {
   295  			req.Header.Set(k, v)
   296  		}
   297  	}
   298  	// COOKIE
   299  	if len(c.cookies) > 0 {
   300  		headerCookie := ""
   301  		for k, v := range c.cookies {
   302  			if len(headerCookie) > 0 {
   303  				headerCookie += ";"
   304  			}
   305  			headerCookie += k + "=" + v
   306  		}
   307  		if len(headerCookie) > 0 {
   308  			req.Header.Set("Cookie", headerCookie)
   309  		}
   310  	}
   311  	// 执行请求
   312  	resp := (*http.Response)(nil)
   313  	for {
   314  		if r, err := c.Do(req); err != nil {
   315  			if c.retryCount > 0 {
   316  				c.retryCount--
   317  			} else {
   318  				return nil, err
   319  			}
   320  		} else {
   321  			resp = r
   322  			break
   323  		}
   324  	}
   325  	r := &ClientResponse{
   326  		cookies: make(map[string]string),
   327  	}
   328  	r.Response = resp
   329  	// 浏览器模式
   330  	if c.browserMode {
   331  		now := time.Now()
   332  		for _, v := range r.Cookies() {
   333  			if v.Expires.UnixNano() < now.UnixNano() {
   334  				delete(c.cookies, v.Name)
   335  			} else {
   336  				c.cookies[v.Name] = v.Value
   337  			}
   338  		}
   339  	}
   340  	//fmt.Println(url, c.cookies)
   341  	return r, nil
   342  }