123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- package requests
- import (
- "bytes"
- "encoding/json"
- "errors"
- "io"
- "io/ioutil"
- "mime/multipart"
- "net/http"
- "net/url"
- "os"
- "strings"
- )
- // Client: 封装了http的参数等信息
- type Client struct {
- // 自定义Client
- client *http.Client
- url string
- method string
- header http.Header
- params url.Values
- form url.Values
- json interface{}
- multipart FileForm
- }
- // FileForm: form参数和文件参数
- type FileForm struct {
- Value url.Values
- File map[string]string
- }
- // Result: http响应结果
- type Result struct {
- Resp *http.Response
- Err error
- }
- // Get: http `GET` 请求
- func Get(url string) *Client {
- return newClient(url, http.MethodGet, nil)
- }
- // Post: http `POST` 请求
- func Post(url string) *Client {
- return newClient(url, http.MethodPost, nil)
- }
- // Put: http `PUT` 请求
- func Put(url string) *Client {
- return newClient(url, http.MethodPut, nil)
- }
- // Delete: http `DELETE` 请求
- func Delete(url string) *Client {
- return newClient(url, http.MethodDelete, nil)
- }
- // Request: 用于自定义请求方式,比如`HEAD`、`PATCH`、`OPTIONS`、`TRACE`
- // client参数用于替换DefaultClient,如果为nil则会使用默认的
- func Request(url, method string, client *http.Client) *Client {
- return newClient(url, method, client)
- }
- // Params: http请求中url参数
- func (c *Client) Params(params url.Values) *Client {
- for k, v := range params {
- c.params[k] = v
- }
- return c
- }
- // Header: http请求头
- func (c *Client) Header(k, v string) *Client {
- c.header.Set(k, v)
- return c
- }
- // Headers: http请求头
- func (c *Client) Headers(header http.Header) *Client {
- for k, v := range header {
- c.header[k] = v
- }
- return c
- }
- // Form: 表单提交参数
- func (c *Client) Form(form url.Values) *Client {
- c.header.Set("Content-Type", "application/x-www-form-urlencoded")
- c.form = form
- return c
- }
- // Json: json提交参数
- func (c *Client) Json(json interface{}) *Client {
- c.header.Set("Content-Type", "application/json")
- c.json = json
- return c
- }
- // Multipart: form-data提交参数
- func (c *Client) Multipart(multipart FileForm) *Client {
- c.multipart = multipart
- return c
- }
- // Send: 发送http请求
- func (c *Client) Send() *Result {
- var result *Result
- if c.params != nil && len(c.params) != 0 {
- // 如果url中已经有query string参数,则只需要&拼接剩下的即可
- encoded := c.params.Encode()
- if strings.Index(c.url, "?") == -1 {
- c.url += "?" + encoded
- } else {
- c.url += "&" + encoded
- }
- }
- contentType := c.header.Get("Content-Type")
- if c.multipart.Value != nil || c.multipart.File != nil {
- result = c.createMultipartForm()
- } else if strings.HasPrefix(contentType, "application/json") {
- result = c.createJson()
- } else if strings.HasPrefix(contentType, "application/x-www-form-urlencoded") {
- result = c.createForm()
- } else {
- result = c.createEmptyBody()
- }
- return result
- }
- // form-data
- func (c *Client) createMultipartForm() *Result {
- var result = new(Result)
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
- for name, filename := range c.multipart.File {
- file, err := os.Open(filename)
- if err != nil {
- result.Err = err
- return result
- }
- part, err := writer.CreateFormFile(name, filename)
- if err != nil {
- result.Err = err
- return result
- }
- // todo 这里的io.Copy实现,会把file文件都读取到内存里面,然后当做一个buffer传给NewRequest。对于大文件来说会占用很多内存
- _, err = io.Copy(part, file)
- if err != nil {
- result.Err = err
- return result
- }
- err = file.Close()
- if err != nil {
- result.Err = err
- return result
- }
- }
- for name, values := range c.multipart.Value {
- for _, value := range values {
- _ = writer.WriteField(name, value)
- }
- }
- err := writer.Close()
- if err != nil {
- result.Err = err
- return result
- }
- req, err := http.NewRequest(c.method, c.url, body)
- req.Header = c.header
- req.Header.Set("Content-Type", writer.FormDataContentType())
- c.doSend(req, result)
- return result
- }
- // application/json
- func (c *Client) createJson() *Result {
- var result = new(Result)
- b, err := json.Marshal(c.json)
- if err != nil {
- result.Err = err
- return result
- }
- req, err := http.NewRequest(c.method, c.url, bytes.NewReader(b))
- if err != nil {
- result.Err = err
- return result
- }
- req.Header = c.header
- c.doSend(req, result)
- return result
- }
- // application/x-www-form-urlencoded
- func (c *Client) createForm() *Result {
- var result = new(Result)
- form := c.form.Encode()
- req, err := http.NewRequest(c.method, c.url, strings.NewReader(form))
- if err != nil {
- result.Err = err
- return result
- }
- req.Header = c.header
- c.doSend(req, result)
- return result
- }
- // none http body
- func (c *Client) createEmptyBody() *Result {
- var result = new(Result)
- req, err := http.NewRequest(c.method, c.url, nil)
- if err != nil {
- result.Err = err
- return result
- }
- req.Header = c.header
- c.doSend(req, result)
- return result
- }
- func (c *Client) doSend(req *http.Request, result *Result) {
- if c.client != nil {
- result.Resp, result.Err = c.client.Do(req)
- return
- }
- result.Resp, result.Err = http.DefaultClient.Do(req)
- }
- // StatusOk: 判断http响应码是否为200
- func (r *Result) StatusOk() *Result {
- if r.Err != nil {
- return r
- }
- if r.Resp.StatusCode != http.StatusOK {
- r.Err = errors.New("status code is not 200")
- return r
- }
- return r
- }
- // Status2xx: 判断http响应码是否为2xx
- func (r *Result) Status2xx() *Result {
- if r.Err != nil {
- return r
- }
- if r.Resp.StatusCode < http.StatusOK || r.Resp.StatusCode >= http.StatusMultipleChoices {
- r.Err = errors.New("status code is not match [200, 300)")
- return r
- }
- return r
- }
- // Raw: 获取http响应内容,返回字节数组
- func (r *Result) Raw() ([]byte, error) {
- if r.Err != nil {
- return nil, r.Err
- }
- b, err := ioutil.ReadAll(r.Resp.Body)
- if err != nil {
- r.Err = err
- return nil, r.Err
- }
- defer r.Resp.Body.Close()
- return b, r.Err
- }
- // Text: 获取http响应内容,返回字符串
- func (r *Result) Text() (string, error) {
- b, err := r.Raw()
- if err != nil {
- r.Err = err
- return "", r.Err
- }
- return string(b), nil
- }
- // Json: 获取http响应内容,返回json
- func (r *Result) Json(v interface{}) error {
- b, err := r.Raw()
- if err != nil {
- r.Err = err
- return r.Err
- }
- return json.Unmarshal(b, v)
- }
- // Save: 获取http响应内容,保存为文件
- func (r *Result) Save(name string) error {
- if r.Err != nil {
- return r.Err
- }
- f, err := os.Create(name)
- if err != nil {
- r.Err = err
- return r.Err
- }
- defer f.Close()
- _, err = io.Copy(f, r.Resp.Body)
- if err != nil {
- r.Err = err
- return r.Err
- }
- defer r.Resp.Body.Close()
- return nil
- }
- func newClient(u string, method string, client *http.Client) *Client {
- return &Client{
- client: client,
- url: u,
- method: method,
- header: make(http.Header),
- params: make(url.Values),
- form: make(url.Values),
- }
- }
|