{"id":13461531,"url":"https://github.com/guonaihong/gout","last_synced_at":"2025-05-14T03:09:40.923Z","repository":{"id":39121608,"uuid":"190199903","full_name":"guonaihong/gout","owner":"guonaihong","description":"gout to become the Swiss Army Knife of the http client @^^@---\u003e  gout 是http client领域的瑞士军刀，小巧，强大，犀利。具体用法可看文档，如使用迷惑或者API用得不爽都可提issues","archived":false,"fork":false,"pushed_at":"2025-03-12T23:26:52.000Z","size":656,"stargazers_count":1333,"open_issues_count":30,"forks_count":114,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-04-10T19:53:45.407Z","etag":null,"topics":["go","golang","http-client","http-request","http-requests","httpclient","request"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guonaihong.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-06-04T12:47:38.000Z","updated_at":"2025-04-09T06:21:16.000Z","dependencies_parsed_at":"2023-02-17T06:55:29.241Z","dependency_job_id":"0b06927a-3763-4cff-92d2-b30a3f7d72bc","html_url":"https://github.com/guonaihong/gout","commit_stats":{"total_commits":282,"total_committers":17,"mean_commits":16.58823529411765,"dds":0.07446808510638303,"last_synced_commit":"404fba63c74769b3eedd683f1a9a52a6672d676c"},"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guonaihong%2Fgout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guonaihong%2Fgout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guonaihong%2Fgout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guonaihong%2Fgout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guonaihong","download_url":"https://codeload.github.com/guonaihong/gout/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254059510,"owners_count":22007769,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","http-client","http-request","http-requests","httpclient","request"],"created_at":"2024-07-31T11:00:42.497Z","updated_at":"2025-05-14T03:09:40.897Z","avatar_url":"https://github.com/guonaihong.png","language":"Go","funding_links":[],"categories":["开源类库","Go","Open source library","Go (531)","Repositories"],"sub_categories":["HTTP","HTTP Print Test"],"readme":"# gout\n\ngout 是go写的http 客户端，为提高工作效率而开发\n\n![Go](https://github.com/guonaihong/gout/workflows/Go/badge.svg)\n[![codecov](https://codecov.io/gh/guonaihong/gout/branch/master/graph/badge.svg)](https://codecov.io/gh/guonaihong/gout)\n[![Go Report Card](https://goreportcard.com/badge/github.com/guonaihong/gout)](https://goreportcard.com/report/github.com/guonaihong/gout)\n\n## 构架\n\n![gout-ad.png](https://github.com/guonaihong/images/blob/master/gout/gout-v0.0.8.png?raw=true)\n\n## feature\n\n* 支持设置 GET/PUT/DELETE/PATH/HEAD/OPTIONS\n* 支持设置请求 http header(可传 struct,map,array,slice 等类型)\n* 支持设置 URL query(可传 struct,map,array,slice,string 等类型)\n* 支持设置 json 编码到请求 body 里面(可传struct, map, string, []byte 等类型)\n* 支持设置 xml 编码到请求 body 里面(可传struct, string, []byte 等类型)\n* 支持设置 yaml 编码到请求 body 里面(可传struct, map, string, []byte 等类型)\n* 支持设置 protobuf 编码到请求 body里面(可传struct)\n* 支持设置 form-data(可传 struct, map, array, slice 等类型)\n* 支持设置 x-www-form-urlencoded(可传 struct,map,array,slice 等类型)\n* 支持设置 io.Reader，uint/uint8/uint16...int/int8...string...[]byte...float32,float64 至请求 body 里面\n* 支持解析响应body里面的json,xml,yaml至结构体里(BindJSON/BindXML/BindYAML)\n* 支持解析响应body的内容至io.Writer, uint/uint8...int/int8...string...[]byte...float32,float64\n* 支持解析响应header至结构体里\n* 支持接口性能benchmark，可控制压测一定次数还是时间，可控制压测频率\n* 支持retry-backoff，可以指定重试条件\n* 支持发送裸http数据包\n* 支持导出curl命令\n* 传入自定义*http.Client\n* 支持请求中间件(\u003chttps://github.com/antlabs/gout-middleware\u003e)\n* 支持响应中间件ResponseUse\n* 支持设置chunked数据格式发送\n* 支持body, header的数据校验\n* 支持通过build tag自由选择不同的json序列化方式(可选jsoniter,go_json,sonic等)\n* 等等更多\n\n## 演示\n\n\u003cdetails\u003e\n\n![gout-example.gif](https://raw.githubusercontent.com/guonaihong/images/master/gout/gout-example.gif)\n\u003c/details\u003e\n\n## 内容\n\n* [Installation](#installation)\n\n* [example](#example)\n* [quick start](#quick-start)\n* [API Examples](#api-examples)\n  * [GET POST PUT DELETE PATH HEAD OPTIONS](#get-post-put-delete-path-head-options)\n    * [GET POST PUT DELETE PATH HEAD OPTIONS template](#get-post-put-delete-path-head-options-template)\n  * [Query Parameters](#query-parameters)\n  * [http header](#http-header)\n    *[Do not convert http headers](#do-not-convert-http-headers)\n    * [Set request header](#set-request-header)\n    *[Parsing the response header](#parsing-the-response-header)\n    * [get all header](#get-all-header)\n    * [data valid](#data-valid)\n    * [responseUse](#responseuse)\n  * [http body](#http-body)\n    * [body](#body)\n      * [Set the data to the http request body](#set-the-data-to-the-http-request-body)\n      * [Parse the response body into a variable](#parse-the-response-body-into-a-variable)\n    * [json](#json)\n      * [Serialize json to request body](#serialize-json-to-request-body)\n      * [Parsed http response body in json format](#parsed-http-response-body-in-json-format)\n      * [Do not escape html characters](#do-not-escape-html-characters)\n    * [yaml](#yaml)\n    * [xml](#xml)\n    * [form-data](#form-data)\n    * [x-www-form-urlencoded](#x-www-form-urlencoded)\n      * [protobuf](#protobuf)\n    * [callback](#callback)\n      * [get *http.Response](#get-response)\n      * [multiple binding functions](#multiple-binding-functions)\n      * [Auto decode body](#auto-decode-body)\n    * [Set request timeout](#set-request-timeout)\n  * [proxy](#proxy)\n    * [socks5](#socks5)\n  * [cookie](#cookie)\n    * [basic auth](#basic-auth)\n  * [context](#context)\n    * [Cancel a sending request](#cancel-a-sending-request)\n  * [unix socket](#unix-socket)\n  * [http2 doc](#http2-doc)\n  * [debug mode](#debug-mode)\n    * [Turn on debug mode](#turn-on-debug-mode)\n    * [Turn off color highlighting in debug mode](#turn-off-color-highlighting-in-debug-mode)\n      * [Custom debug mode](#custom-debug-mode)\n      * [trace info](#trace-info)\n      * [save to writer](#save-to-writer)\n      * [save to file](#save-to-file)\n      * [Extracting trace information](#extracting-trace-information)\n    * [benchmark](#benchmark)\n      * [benchmarking a certain number of times](#benchmarking-a-certain-number-of-times)\n      * [benchmarking for a certain time](#benchmark-duration)\n      * [benchmarking at a fixed frequency](#benchmark-rate)\n      * [Custom benchmark functions](#custom-benchmark-functions)\n    * [retry backoff](#retry-backoff)\n      * [Specify the retry conditions, when equal to a certain http code](#retry-conditions-httpcode)\n      * [Specify retry conditions. The default URL cannot be accessed. Use the backup URL](#retry-conditions-backupurl)\n    * [import](#import)\n      * [send raw http request](#send-raw-http-request)\n    * [export](#export)\n      * [generate curl command](#generate-curl-command)\n      * [generate curl command and send HTTP request](#generate-curl-command-and-send-http-request)\n    * [Incoming custom * http.Client](#Incoming-custom-*http.Client)\n    * [Using chunked data format](#using-chunked-data-format)\n    * [NewWithOpt](#newwithopt)\n      * [Insecure skip verify](#insecure-skip-verify)\n      * [Turn off 3xx status code automatic jump](#turn-off-3xx-status-code-automatic-jump)\n      * [NewWithOpt set timeout](#new-with-opt-set-timeout)\n      * [NewWithOpt unix sock](#new-with-opt-unix-socket)\n    * [NewWithOpt proxy](#new-with-opt-proxy)\n      * [NewWithOpt socks5](#new-with-opt-socks5)\n    * [Global configuration](#global-configuration)\n      * [set timeout](#set-timeout)\n      * [set debug](#set-debug)\n* [Unique features](#unique-features)\n  * [forward gin data](#forward-gin-data)\n\n* [FAQ](#faq)\n\n## Installation\n\n```\ngo get github.com/guonaihong/gout\n```\n\n# example\n\n [examples](./_example) 目录下面的例子，都是可以直接跑的。如果觉得运行例子还是不明白用法，可以把你迷惑的地方写出来，然后提[issue](https://github.com/guonaihong/gout/issues/new)\n\n### 运行命令如下\n\n ```bash\n cd _example\n # GOPROXY 是打开go module代理，可以更快下载模块\n # 第一次运行需要加GOPROXY下载模块，模块已安装的直接 go run 01-color-json.go 即可\n env GOPROXY=https://goproxy.cn go run 01-color-json.go\n ```\n\n### build tag\n\nGout默认使用语言内置的`encoding/json`包。但是如果你想使用其他的json包，可以通过`build tag`来修改。\n\n[jsoniter](https://github.com/json-iterator/go)\n\n```sh\ngo build -tags=jsoniter .\n```\n\n[go-json](https://github.com/goccy/go-json)\n\n```sh\ngo build -tags=go_json .\n```\n\n[sonic](https://github.com/bytedance/sonic)\n\n```sh\ngo build -tags=\"sonic avx\" .\n```\n\n# quick start\n\n ```go\n package main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"time\"\n)\n\n// 用于解析 服务端 返回的http body\ntype RspBody struct {\n ErrMsg  string `json:\"errmsg\"`\n ErrCode int    `json:\"errcode\"`\n Data    string `json:\"data\"`\n}\n\n// 用于解析 服务端 返回的http header\ntype RspHeader struct {\n Sid  string `header:\"sid\"`\n Time int    `header:\"time\"`\n}\n\nfunc main() {\n rsp := RspBody{}\n header := RspHeader{}\n\n //code := 0\n err := gout.\n\n  // POST请求\n  POST(\"127.0.0.1:8080\").\n\n  // 打开debug模式\n  Debug(true).\n\n  // 设置查询字符串\n  SetQuery(gout.H{\"page\": 10, \"size\": 10}).\n\n  // 设置http header\n  SetHeader(gout.H{\"X-IP\": \"127.0.0.1\", \"sid\": fmt.Sprintf(\"%x\", time.Now().UnixNano())}).\n\n  // SetJSON设置http body为json\n  // 同类函数有SetBody, SetYAML, SetXML, SetForm, SetWWWForm\n  SetJSON(gout.H{\"text\": \"gout\"}).\n\n  // BindJSON解析返回的body内容\n  // 同类函数有BindBody, BindYAML, BindXML\n  BindJSON(\u0026rsp).\n\n  // 解析返回的http header\n  BindHeader(\u0026header).\n  // http code\n  // Code(\u0026code).\n\n  // 结束函数\n  Do()\n\n  // 判断错误\n if err != nil {\n  fmt.Printf(\"send fail:%s\\n\", err)\n }\n}\n\n/*\n\u003e POST /?page=10\u0026size=10 HTTP/1.1\n\u003e Sid: 15d9b742ef32c130\n\u003e X-Ip: 127.0.0.1\n\u003e Content-Type: application/json\n\u003e\n\n{\n    \"text\": \"gout\"\n}\n\n\n*/\n ```\n\n[返回](#内容)\n\n# API examples\n\n## GET POST PUT DELETE PATH HEAD OPTIONS\n\n```go\npackage main\n\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n url := \"https://github.com\"\n // 发送GET方法\n gout.GET(url).Do()\n\n // 发送POST方法\n gout.POST(url).Do()\n\n // 发送PUT方法\n gout.PUT(url).Do()\n\n // 发送DELETE方法\n gout.DELETE(url).Do()\n\n // 发送PATH方法\n gout.PATCH(url).Do()\n\n // 发送HEAD方法\n gout.HEAD(url).Do()\n\n // 发送OPTIONS\n gout.OPTIONS(url).Do()\n}\n\n```\n\n[返回](#内容)\n\n## GET POST PUT DELETE PATH HEAD OPTIONS template\n\n```go\npackage main\n\nimport (\n \"github.com/guonaihong/gout\"\n)\n\ntype testURLTemplateCase struct {\n Host   string\n}\n\nfunc main() {\n\n url := \"https://{{.Host}}\"\n // 发送GET方法\n gout.GET(url, testURLTemplateCase{Host:\"www.qq.com\"}).Do()\n\n // 发送POST方法\n gout.POST(url, testURLTemplateCase{Host:\"www.github.com\"}).Do()\n\n // 发送PUT方法\n gout.PUT(url, testURLTemplateCase{Host:\"www.baidu.com\"}).Do()\n\n // 发送DELETE方法\n gout.DELETE(url, testURLTemplateCase{Host:\"www.google.com\"}).Do()\n\n // 发送PATH方法\n gout.PATCH(url, testURLTemplateCase{Host:\"www.google.com\"}).Do()\n\n // 发送HEAD方法\n gout.HEAD(url, testURLTemplateCase{Host:\"www.google.com\"}).Do()\n\n // 发送OPTIONS\n gout.OPTIONS(url, testURLTemplateCase{Host:\"www.google.com\"}).Do()\n}\n\n```\n\n[返回](#内容)\n\n## Query Parameters\n\n### SetQuery\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n    \"time\"\n)\n\nfunc main() {\n    err := gout.\n        //设置GET请求和url，:8080/test.query是127.0.0.1:8080/test.query的简写\n        GET(\":8080/test.query\").\n        //打开debug模式\n        Debug(true).\n        //设置查询字符串\n        SetQuery(gout.H{\n            \"q1\": \"v1\",\n            \"q2\": 2,\n            \"q3\": float32(3.14),\n            \"q4\": 4.56,\n            \"q5\": time.Now().Unix(),\n            \"q6\": time.Now().UnixNano(),\n            \"q7\": time.Now().Format(\"2006-01-02\")}).\n        //结束函数\n        Do()\n    if err != nil {\n        fmt.Printf(\"%s\\n\", err)\n        return\n    }\n\n}\n\n/*\n\u003e GET /test.query?q1=v1\u0026q2=2\u0026q3=3.14\u0026q4=4.56\u0026q5=1574081600\u0026q6=1574081600258009213\u0026q7=2019-11-18 HTTP/1.1\n\u003e\n\n\u003c HTTP/1.1 200 OK\n\u003c Content-Length: 0\n*/\n\n\n```\n\n[返回](#内容)\n\n### SetQuery支持的更多数据类型\n\n```go\npackage main\n\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n\n code := 0\n\n err := gout.\n\n  //发送GET请求 :8080/testquery是127.0.0.1:8080/testquery简写\n  GET(\":8080/testquery\").\n\n  // 设置查询字符串\n  SetQuery( /*看下面支持的情况*/ ).\n\n  //解析http code，如不关心服务端返回状态吗，不设置该函数即可\n  Code(\u0026code).\n  Do()\n if err != nil {\n\n }\n}\n\n\n\n/*\nSetQuery支持的类型有\n* string\n* map[string]interface{}，可以使用gout.H别名\n* struct\n* array, slice(长度必须是偶数)\n*/\n\n// 1.string\nSetQuery(\"check_in=2019-06-18\u0026check_out=2018-06-18\")\n\n// 2.gout.H 或者 map[string]interface{}\nSetQuery(gout.H{\n    \"check_in\":\"2019-06-18\",\n    \"check_out\":\"2019-06-18\",\n})\n\n// 3.struct\ntype testQuery struct {\n    CheckIn string `query:checkin`\n    CheckOut string `query:checkout`\n}\n\nSetQuery(\u0026testQuery{CheckIn:2019-06-18, CheckOut:2019-06-18})\n\n// 4.array or slice\n// ?active=enable\u0026action=drop\nSetQuery([]string{\"active\", \"enable\", \"action\", \"drop\"})`\n```\n\n\u003c/details\u003e\n\n[返回](#内容)\n\n## http header\n\n#### Do not convert http headers\n\n与SetHeader API唯一的区别就是不修改header名. 大部分情况用SetHeader，如果有不修改header的需求再使用SetHeaderRaw。\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n    \"time\"\n)\n\nfunc main() {\n    err := gout.\n        //设置GET请求和url，:8080/test.header是127.0.0.1:8080/test.header的简写\n        GET(\":8080/test.header\").\n        //设置debug模式\n        Debug(true).\n        //设置请求http header\n        SetHeaderRaw(gout.H{\n            \"h1\": \"v1\",\n            \"h2\": 2,\n            \"h3\": float32(3.14),\n            \"h4\": 4.56,\n            \"h5\": time.Now().Unix(),\n            \"h6\": time.Now().UnixNano(),\n            \"h7\": time.Now().Format(\"2006-01-02\")}).\n        Do()\n    if err != nil {\n        fmt.Printf(\"%s\\n\", err)\n        return\n    }\n\n}\n\n/*\n\u003e GET /test.header HTTP/1.1\n\u003e h2: 2\n\u003e h3: 3.14\n\u003e h4: 4.56\n\u003e h5: 1574081686\n\u003e h6: 1574081686471347098\n\u003e h7: 2019-11-18\n\u003e h1: v1\n\u003e\n\n\n\u003c HTTP/1.1 200 OK\n\u003c Content-Length: 0\n*/\n```\n\n[返回](#内容)\n\n#### Set request header\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n    \"time\"\n)\n\nfunc main() {\n    err := gout.\n        //设置GET请求和url，:8080/test.header是127.0.0.1:8080/test.header的简写\n        GET(\":8080/test.header\").\n        //设置debug模式\n        Debug(true).\n        //设置请求http header\n        SetHeader(gout.H{\n            \"h1\": \"v1\",\n            \"h2\": 2,\n            \"h3\": float32(3.14),\n            \"h4\": 4.56,\n            \"h5\": time.Now().Unix(),\n            \"h6\": time.Now().UnixNano(),\n            \"h7\": time.Now().Format(\"2006-01-02\")}).\n        Do()\n    if err != nil {\n        fmt.Printf(\"%s\\n\", err)\n        return\n    }\n\n}\n\n/*\n\u003e GET /test.header HTTP/1.1\n\u003e H2: 2\n\u003e H3: 3.14\n\u003e H4: 4.56\n\u003e H5: 1574081686\n\u003e H6: 1574081686471347098\n\u003e H7: 2019-11-18\n\u003e H1: v1\n\u003e\n\n\n\u003c HTTP/1.1 200 OK\n\u003c Content-Length: 0\n*/\n```\n\n[返回](#内容)\n\n#### Parsing the response header\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n    \"time\"\n)\n\n// 和解析json类似，如要解析http header需设置header tag\ntype rspHeader struct {\n    Total int       `header:\"total\"`\n    Sid   string    `header:\"sid\"`\n    Time  time.Time `header:\"time\" time_format:\"2006-01-02\"`\n}\n\nfunc main() {\n\n    rsp := rspHeader{}\n    err := gout.\n        // :8080/test.header是 http://127.0.0.1:8080/test.header的简写\n        GET(\":8080/test.header\").\n        //打开debug模式\n        Debug(true).\n        //解析请求header至结构体中\n        BindHeader(\u0026rsp). \n        //结束函数\n        Do()\n    if err != nil {\n        fmt.Printf(\"%s\\n\", err)\n        return\n    }\n\n    fmt.Printf(\"rsp header:\\n%#v \\nTime:%s\\n\", rsp, rsp.Time)\n}\n\n/*\n\u003e GET /test.header HTTP/1.1\n\u003e\n\n\n\n\u003c HTTP/1.1 200 OK\n\u003c Content-Length: 0\n\u003c Sid: 1234\n\u003c Time: 2019-11-18\n\u003c Total: 2048\n*/\n\n```\n\n[返回](#内容)\n\n### SetHeader和BindHeader支持的更多类型\n\n\u003cdetails\u003e\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n)\n\ntype testHeader struct {\n    CheckIn  string `header:checkin`\n    CheckOut string `header:checkout`\n}\n\nfunc main() {\n\n    t := testHeader{}\n\n    code := 0\n\n    err := gout.\n        GET(\":8080/testquery\").\n        Code(\u0026code).\n        SetHeader( /*看下面支持的类型*/ ).\n        BindHeader(\u0026t).\n        Do()\n    if err != nil {\n        fmt.Printf(\"fail:%s\\n\", err)\n    }   \n}\n\n```\n\n* BindHeader支持的类型有\n结构体\n\n```go\n// struct\ntype testHeader struct {\n    CheckIn string `header:checkin`\n    CheckOut string `header:checkout`\n}\n```\n\n* SetHeader支持的类型有\n\n```go\n/*\nmap[string]interface{}，可以使用gout.H别名\nstruct\narray, slice(长度必须是偶数)\n*/\n\n// gout.H 或者 map[string]interface{}\nSetHeader(gout.H{\n    \"check_in\":\"2019-06-18\",\n    \"check_out\":\"2019-06-18\",\n})\n\n// struct\ntype testHeader struct {\n    CheckIn string `header:checkin`\n    CheckOut string `header:checkout`\n}\n\nSetHeader(\u0026testHeader{CheckIn:2019-06-18, CheckOut:2019-06-18})\n\n// array or slice\n// -H active:enable -H action:drop\nSetHeader([]string{\"active\", \"enable\", \"action\", \"drop\"})\n```\n\n\u003c/details\u003e\n\n[返回](#内容)\n\n## data valid\n\n数据校验使用 \u003chttps://github.com/go-playground/validator\u003e 完成功能, 更多语法可看该文档.\n\n```go\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\ntype testValid struct {        \n        Val string `valid:\"required\"`   \n} \n\nfunc main() {\n tv := testValid{}\n err := gout.\n  // 设置POST方法和url\n  POST(\":8080/req/body\").\n  //打开debug模式\n  Debug(true).\n  //解析json, 并且当需要的字段没有值时, 返回错误\n  BindJSON(\u0026tv).\n  //结束函数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%s\\n\", err)\n  return\n }\n\n}\n```\n\n[返回](#内容)\n\n## responseUse\n\nresponse中间件，在Bind()之前执行。可以对response进行通用逻辑处理。\n如果只需要闭包逻辑，则可以使用`WithResponseMiddlerFunc`，而不必创建一个结构体，下面的例子中对两种方法都进行了使用。\n\n```go\n\n\nimport (\n \"bytes\"\n \"encoding/json\"\n \"errors\"\n \"fmt\"\n \"github.com/gin-gonic/gin\"\n \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/middler\"\n \"io/ioutil\"\n \"log\"\n \"net/http\"\n \"time\"\n)\n\n// response拦截器修改示例\ntype demoResponseMiddler struct{}\n\nfunc (d *demoResponseMiddler) ModifyResponse(response *http.Response) error {\n // 修改responseBody。 因为返回值大概率会有 { code, data,msg} 等字段,希望进行统一处理\n //这里想验证code. 如果不对就返回error。 对的话将 data中的内容写入body,这样后面BindJson的时候就可以直接处理业务了\n all, err := ioutil.ReadAll(response.Body)\n if err != nil {\n  return err\n }\n obj := make(map[string]interface{})\n err = json.Unmarshal(all, \u0026obj)\n if err != nil {\n  return err\n }\n code := obj[\"code\"]\n msg := obj[\"msg\"]\n data := obj[\"data\"]\n\n // Go中json中的数字经过反序列化会成为float64类型\n if float64(200) != code {\n  return errors.New(fmt.Sprintf(\"请求失败, code %d msg %s\", code, msg))\n } else {\n  byt, _ := json.Marshal(\u0026data)\n  response.Body = ioutil.NopCloser(bytes.NewReader(byt))\n  return nil\n }\n}\nfunc demoResponse() *demoResponseMiddler {\n return \u0026demoResponseMiddler{}\n}\n\nfunc main() {\n go server()                        //等会起测试服务\n time.Sleep(time.Millisecond * 500) //用时间做个等待同步\n responseUseExample()\n}\n\nfunc responseUseExample() {\n //成功请求\n successRes := new(map[string]interface{})\n err := gout.GET(\":8080/success\").ResponseUse(\n  demoResponse(),\n  // 注意这里使用了WithResponseMiddlerFunc\n  middler.WithResponseMiddlerFunc(func(response *http.Response) error {\n   // Do your magic\n   return nil\n  }),\n ).BindJSON(\u0026successRes).Do()\n log.Printf(\"success请求  --\u003e   响应 %s  \\n  err  %s \\n \", successRes, err)\n\n //fail请求\n failRes := new(map[string]interface{})\n err = gout.GET(\":8080/fail\").ResponseUse(demoResponse()).BindJSON(\u0026failRes).Do()\n log.Printf(\"fail请求  --\u003e   响应 %s  \\n  err  %s \\n \", successRes, err)\n}\n\ntype Result struct {\n Code int         `json:\"code\"`\n Msg  string      `json:\"msg\"`\n Data interface{} `json:\"data\"`\n}\ntype Item struct {\n Id   string `json:\"id\"`\n Name string `json:\"name\"`\n}\n\nfunc server() {\n router := gin.New()\n router.GET(\"/success\", func(c *gin.Context) {\n  c.JSON(200, Result{200, \"请求成功了\", Item{\"001\", \"张三\"}})\n })\n router.GET(\"/fail\", func(c *gin.Context) {\n  c.JSON(200, Result{500, \"查询数据库出错了\", nil})\n })\n router.Run()\n}\n```\n\n[返回](#内容)\n\n## http body\n\n### body\n\n#### Set the data to the http request body\n\n```go\n// SetBody 设置string, []byte等类型数据到http body里面\n// SetBody支持的更多数据类型可看下面\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n err := gout.\n  // 设置POST方法和url\n  POST(\":8080/req/body\").\n  //打开debug模式\n  Debug(true).\n  // 设置非结构化数据到http body里面\n  // 设置json需使用SetJSON\n  SetBody(\"send string\").\n  //结束函数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%s\\n\", err)\n  return\n }\n\n}\n\n/*\n\u003e POST /req/body HTTP/1.1\n\u003e\n\nsend string\n\n\u003c HTTP/1.1 200 OK\n\u003c Content-Type: text/plain; charset=utf-8\n\u003c Content-Length: 2\n\n*/\n\n```\n\n[返回](#内容)\n\n#### get all header\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"net/http\"\n)\n\nfunc main() {\n header := make(http.Header)\n err := gout.\n  // 设置POST方法和url\n  POST(\":8080/req/body\").\n  //打开debug模式\n  Debug(true).\n  // 获取所有的响应http header\n  BindHeader(\u0026header).\n  //结束函数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%s\\n\", err)\n  return\n }\n\n}\n```\n\n[返回](#内容)\n\n#### Parse the response body into a variable\n\n```go\n// BindBody bind body到string, []byte等类型变量里面\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n s := \"\"\n err := gout.\n  // 设置GET 方法及url\n  GET(\"www.baidu.com\").\n  // 绑定返回值\n  BindBody(\u0026s).\n  // 结束函数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%s\\n\", err)\n  return\n }\n\n fmt.Printf(\"html size = %d\\n\", len(s))\n}\n\n```\n\n[返回](#内容)\n\n#### 支持的类型有\n\n* io.Reader(SetBody 支持)\n* io.Writer(BindBody 支持)\n* int, int8, int16, int32, int64\n* uint, uint8, uint16, uint32, uint64\n* string\n* []byte\n* float32, float64\n\n#### 明确不支持的类型有\n\n* struct\n* array, slice\n\n### json\n\n#### Serialize json to request body\n\n[更多支持数据类型及用法](./_example/01-color-json.go)\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n err := gout.POST(\":8080/colorjson\").\n  //打开debug模式\n  Debug(true).\n  //设置json到请求body\n  SetJSON(\n   gout.H{\n    \"str\":   \"foo\",\n    \"num\":   100,\n    \"bool\":  false,\n    \"null\":  nil,\n    \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n    \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n   },\n  ).\n  Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n\n/*\n\u003e POST /colorjson HTTP/1.1\n\u003e Content-Type: application/json\n\u003e\n\n{\n    \"array\": [\n        \"foo\",\n        \"bar\",\n        \"baz\"\n    ],\n    \"bool\": false,\n    \"null\": null,\n    \"num\": 100,\n    \"obj\": {\n        \"a\": 1,\n        \"b\": 2\n    },\n    \"str\": \"foo\"\n}\n*/\n\n```\n\n[返回](#内容)\n\n#### Parsed http response body in json format\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\ntype rsp struct {\n ErrMsg  string `json:\"errmsg\"`\n ErrCode int    `json:\"errcode\"`\n}\n\nfunc main() {\n rsp := rsp{}\n err := gout.\n  GET(\":8080/colorjson\").\n  //打开debug模式\n  Debug(true).\n  //绑定响应json数据到结构体\n  BindJSON(\u0026rsp).\n  //结束函数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n#### do not escape html characters\n\n* SetJSONNotEscape 和SetJSON唯一的区别就是不转义HTML字符\n\n```go\nerr := gout.POST(ts.URL).\n   Debug(true).\n   SetJSONNotEscape(gout.H{\"url\": \"http://www.com?a=b\u0026c=d\"}).\n   Do()\n\n//\u003e POST / HTTP/1.1\n//\u003e Content-Type: application/json\n//\u003e\n\n//{\n//    \"url\": \"http://www.com?a=b\u0026c=d\"\n//}\n\n//\u003c HTTP/1.1 200 OK\n//\u003c Date: Sun, 18 Dec 2022 14:05:21 GMT\n//\u003c Content-Length: 0\n```\n\n[返回](#内容)\n\n### yaml\n\n* SetYAML() 设置请求http body为yaml\n* BindYAML() 解析响应http body里面的yaml到结构体里面\n\n发送yaml到服务端，然后把服务端返回的yaml结果解析到结构体里面\n\n```go\ntype data struct {\n    Id int `yaml:\"id\"`\n    Data string `yaml:\"data\"`\n}\n\n\nvar d1, d2 data\nvar httpCode int \n\n\nerr := gout.POST(\":8080/test.yaml\").SetYAML(\u0026d1).BindYAML(\u0026d2).Code(\u0026httpCode).Do()\nif err != nil || httpCode != 200{\n    fmt.Printf(\"send fail:%s\\n\", err)\n}\n\n```\n\n[返回](#内容)\n\n### xml\n\n* SetXML() 设置请求http body为xml\n* BindXML() 解析响应http body里面的xml到结构体里面\n\n发送xml到服务端，然后把服务端返回的xml结果解析到结构体里面\n\n```go\ntype data struct {\n    Id int `xml:\"id\"`\n    Data string `xml:\"data\"`\n}\n\n\nvar d1, d2 data\nvar httpCode int \n\n\nerr := gout.POST(\":8080/test.xml\").SetXML(\u0026d1).BindXML(\u0026d2).Code(\u0026httpCode).Do()\nif err != nil || httpCode != 200{\n    fmt.Printf(\"send fail:%s\\n\", err)\n}\n\n```\n\n[返回](#内容)\n\n### form-data\n\n* SetForm() 设置http body 为multipart/form-data格式数据\n\n客户端发送multipart/form-data到服务端,curl用法等同go代码\n\n```bash\ncurl -F mode=A -F text=\"good\" -F voice=@./test.pcm -f voice2=@./test2.pcm url\n```\n\n* 使用gout.H\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n\n code := 0\n err := gout.\n  POST(\":8080/test\").\n  // 打开debug模式\n  Debug(true).\n  SetForm(\n   gout.H{\n    \"mode\": \"A\",\n    \"text\": \"good\",\n    // 从文件里面打开\n    \"voice\":  gout.FormFile(\"test.pcm\"),\n    \"voice2\": gout.FormMem(\"pcm\"),\n   },\n  ).\n  //解析http code，如不关心可以不设置\n  Code(\u0026code).\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%s\\n\", err)\n }\n\n if code != 200 {\n }\n}\n\n/*\n\u003e POST /test HTTP/1.1\n\u003e Content-Type: multipart/form-data; boundary=2b0685e5b98e540f80b247d5e7c1283807aa07e62b827543859a6db765a8\n\u003e\n\n--2b0685e5b98e540f80b247d5e7c1283807aa07e62b827543859a6db765a8\nContent-Disposition: form-data; name=\"mode\"\n\nA\n--2b0685e5b98e540f80b247d5e7c1283807aa07e62b827543859a6db765a8\nContent-Disposition: form-data; name=\"text\"\n\ngood\n--2b0685e5b98e540f80b247d5e7c1283807aa07e62b827543859a6db765a8\nContent-Disposition: form-data; name=\"voice\"; filename=\"voice\"\nContent-Type: application/octet-stream\n\npcm pcm pcm\n\n--2b0685e5b98e540f80b247d5e7c1283807aa07e62b827543859a6db765a8\nContent-Disposition: form-data; name=\"voice2\"; filename=\"voice2\"\nContent-Type: application/octet-stream\n\npcm\n--2b0685e5b98e540f80b247d5e7c1283807aa07e62b827543859a6db765a8--\n\n\n\u003c HTTP/1.1 200 OK\n\u003c Server: gurl-server\n\u003c Content-Length: 0\n*/\n \n```\n\n* 使用结构体\n\n```go\ntype testForm struct {\n    Mode string `form:\"mode\"`\n    Text string `form:\"text\"`\n    Voice string `form:\"voice\" form-file:\"true\"` //从文件中读取 \n    Voice2 []byte `form:\"voice2\" form-file:\"mem\"`  //从内存中构造\n}\n\ntype rsp struct{\n    ErrMsg string `json:\"errmsg\"`\n    ErrCode int `json:\"errcode\"`\n}\n\nt := testForm{}\nr := rsp{}\ncode := 0\n\nerr := gout.POST(url).SetForm(\u0026t).ShoudBindJSON(\u0026r).Code(\u0026code).Do()\nif err != nil {\n\n}\n```\n\n[返回](#内容)\n\n### x-www-form-urlencoded\n\n* 使用SetWWWForm函数实现发送x-www-form-urlencoded类型数据\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n\n code := 0\n err := gout.\n  POST(\":8080/post\").\n  // 打开debug模式\n  Debug(true).\n  // 设置x-www-form-urlencoded数据\n  SetWWWForm(\n   gout.H{\n    \"int\":     3,\n    \"float64\": 3.14,\n    \"string\":  \"test-www-Form\",\n   },\n  ).\n  // 关心http code 返回值设置\n  Code(\u0026code).\n  Do()\n if err != nil {\n  fmt.Printf(\"%s\\n\", err)\n  return\n }\n\n if code != 200 {\n }\n}\n\n/*\n\u003e POST /post HTTP/1.1\n\u003e Content-Type: application/x-www-form-urlencoded\n\u003e\n\nfloat64=3.14\u0026int=3\u0026string=test-www-Form\n\n\u003c HTTP/1.1 200 OK\n\u003c Content-Length: 0\n\u003c Server: gurl-server\n\n*/\n\n```\n\n[返回](#内容)\n\n### protobuf\n\nSetProtoBuf支持，protobuf序列化后的[]byte，或者生成的protobuf结构体指针\n\n```go\npackage main\n\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n httpCode := 0\n err := GET(\":8080/echo\").\n  SetProtoBuf( /* protobuf 生成的结构体，必须传指针类型*/ ).\n  Code(\u0026httpCode).\n  Do()\n}\n```\n\n[返回](#内容)\n\n### callback\n\ncallback主要用在，服务端会返回多种格式body的场景, 比如404返回的是html, 200返回json。\n这时候要用Callback挂载多种处理函数，处理不同的数据结构\n\n```go\n\nfunc main() {\n \n r, str404 := Result{}, \"\"\n code := 0\n\n err := gout.GET(\":8080\").Callback(func(c *gout.Context) (err error) {\n\n  switch c.Code {\n  case 200: //http code为200时，服务端返回的是json 结构\n   c.BindJSON(\u0026r)\n  case 404: //http code为404时，服务端返回是html 字符串\n   c.BindBody(\u0026str404)\n  }\n  code = c.Code\n  return nil\n\n }).Do()\n\n if err != nil {\n  fmt.Printf(\"err = %s\\n\", err)\n  return\n }\n\n fmt.Printf(\"http code = %d, str404(%s) or json result(%v)\\n\", code, str404, r)\n\n}\n\n```\n\n[返回](#内容)\n\n### get .Response\n\n```go\nfunc main() {\n \n\n resp, err := gout.GET(\":8080\").SetJSON(`{\"test\":\"value\"}`).Response()\n\n if resp != nil {\n  defer resp.Body.Close()\n }\n}\n\n```\n\n[返回](#内容)\n\n### multiple binding functions\n\n支持绑定多个对象, BindXXX函数可以多次调用。例子里面是BindJSON和BindBody\n\n```go\nvar responseStruct struct {\n Name string `json:\"name\"`\n Age  int    `json:\"age\"`\n}\n\nfunc main() {\n\n var responseStr string\n err := gout.GET(\"url\").\n  SetQuery(gout.H{}).\n  BindJSON(\u0026responseStruct).\n  BindBody(\u0026responseStr).\n  Do()\n\n if err != nil {\n  return\n }\n\n log.Println(responseStr)\n}\n\n```\n\n[返回](#内容)\n\n### Auto decode body\n\n响应头里面指明压缩格式，使用AutoDecodeBody接口可以自动解压。\n\n```go\n//Content-Encoding: gzip\n//Content-Encoding: deflate\n//Content-Encoding: br\n//gzip由标准库原生支持，不需要使用AutoDecodeBody接口，后两种由gout支持.\nfunc main() {\n gout.GET(url).AutoDecodeBody().BindBody(\u0026s).Do()\n}\n```\n\n[返回](#内容)\n\n## Set request timeout\n\nsetimeout是request级别的超时方案。相比http.Client级别，更灵活。\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"time\"\n)\n\nfunc main() {\n err := gout.GET(\":8080\").\n  SetTimeout(2 * time.Second).\n  Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n## proxy\n\n* SetProxy 设置代理服务地址\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"log\"\n)\n\nfunc main() {\n c := \u0026http.Client{}\n s := \"\"\n err := gout.\n  New(c).\n  GET(\"www.qq.com\").\n  // 设置proxy服务地址\n  SetProxy(\"http://127.0.0.1:7000\").\n  // 绑定返回数据到s里面\n  BindBody(\u0026s).\n  Do()\n\n if err != nil {\n  log.Println(err)\n  return\n }\n\n fmt.Println(s)\n}\n\n```\n\n[返回](#内容)\n\n## socks5\n\n* SetSOCKS5 设置socks5地址\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n    \"log\"\n    \"net/http\"\n)\n\nfunc main() {\n    c := \u0026http.Client{}\n    s := \"\"\n    err := gout.\n        New(c).\n        GET(\"www.qq.com\").\n        // 设置proxy服务地址\n        SetSOCKS5(\"127.0.0.1:7000\").\n        // 绑定返回数据到s里面\n        BindBody(\u0026s).\n        Do()\n\n    if err != nil {\n        log.Println(err)\n        return\n    }   \n\n    fmt.Println(s)\n}\n```\n\n[返回](#内容)\n\n## cookie\n\n* SetCookies设置cookie, 可以设置一个或者多个cookie\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"net/http\"\n)\n\nfunc main() {\n\n // === 发送多个cookie ====\n err := gout.\n  // :8080/cookie是http://127.0.0.1:8080/cookie的简写\n  GET(\":8080/cookie\").\n  //设置debug模式\n  Debug(true).\n  SetCookies(\n   //设置cookie1\n   \u0026http.Cookie{\n    Name:  \"test1\",\n    Value: \"test1\",\n   },\n   //设置cookie2\n   \u0026http.Cookie{\n    Name:  \"test2\",\n    Value: \"test2\",\n   },\n  ).\n  Do()\n\n if err != nil {\n  fmt.Println(err)\n  return\n }\n\n // === 发送一个cookie ===\n err = gout.\n  // :8080/cookie/one是http://127.0.0.1:8080/cookie/one的简写\n  GET(\":8080/cookie/one\").\n  //设置debug模式\n  Debug(true).\n  SetCookies(\n   //设置cookie1\n   \u0026http.Cookie{\n    Name:  \"test3\",\n    Value: \"test3\",\n   },\n  ).\n  Do()\n fmt.Println(err)\n\n}\n\n```\n\n[返回](#内容)\n\n## basic auth\n\n使用```SetBasicAuth```接口\n\n```go\nfunc main() {\n \n err := gout.POST(\":8080/colorjson\").\n  SetBasicAuth(\"userName\", \"password\").\n  SetJSON(gout.H{\"str\": \"foo\",\n   \"num\":   100,\n   \"bool\":  false,\n   \"null\":  nil,\n   \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n   \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n  }).Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n```\n\n[返回](#内容)\n\n## context\n\n* WithContext设置context，可以取消http请求\n\n### Cancel a sending request\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"github.com/guonaihong/gout\"\n    \"time\"\n)\n\nfunc main() {\n    //　声明一个context\n    ctx, cancel := context.WithCancel(context.Background())\n\n    //调用cancel可取消http请求\n    go func() {\n        time.Sleep(time.Second)\n        cancel()\n    }() \n\n    err := gout.\n        GET(\"127.0.0.1:8080/cancel\"). //设置GET请求以及需要访问的url\n        WithContext(ctx).             //设置context, 外层调用cancel函数就可取消这个http请求\n        Do()\n\n    if err != nil {\n    }   \n}\n\n```\n\n[返回](#内容)\n\n## unix socket\n\n* UnixSocket可以把http底层通信链路由tcp修改为unix domain socket  \n下面的例子，会通过domain socket发送http GET请求，http body的内容是hello world\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n    \"net/http\"\n)\n\nfunc main() {\n    c := http.Client{}\n\n    g := gout.\n        New(\u0026c).\n        UnixSocket(\"/tmp/test.socket\") //设置unixsocket文件位置\n\n    err := g.\n        GET(\"http://a/test\").   //设置GET请求\n        SetBody(\"hello world\"). //设置body内容\n        Do()\n    fmt.Println(err)\n}\n```\n\n[返回](#内容)\n\n## http2 doc\n\ngo 使用https访问http2的服务会自动启用http2协议，这里不需要任何特殊处理\n\n* \u003chttps://http2.golang.org/\u003e (bradfitz建的http2测试网址,里面大约有十来个测试地址，下面的例子选了一个)\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n    s := \"\"\n    err := gout.\n        GET(\"https://http2.golang.org/reqinfo\"). //设置GET请求和请求url\n        Debug(true).                             //打开debug模式，可以看到请求数据和响应数据\n        SetBody(\"hello, ###########\").           //设置请求body的内容，如果你的请求内容是json格式，需要使用SetJSON函数\n        BindBody(\u0026s).                            //解析响应body内容\n        Do()                                     //结束函数\n\n    if err != nil {\n        fmt.Printf(\"send fail:%s\\n\", err)\n    }   \n    _ = s \n}\n\n```\n\n[返回](#内容)\n\n## debug mode\n\n### Turn on debug mode\n\n该模式主要方便调试用的，默认开启颜色高亮(如果要关闭颜色高亮，请往下看)\n\n```go\nfunc main() {\n \n err := gout.POST(\":8080/colorjson\").\n  Debug(true). //打开debug模式\n  SetJSON(gout.H{\"str\": \"foo\",\n   \"num\":   100,\n   \"bool\":  false,\n   \"null\":  nil,\n   \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n   \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n  }).Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n```\n\n[返回](#内容)\n\n### Turn off color highlighting in debug mode\n\n使用debug.NoColor()传入Debug函数关闭颜色高亮\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/debug\"\n)\nfunc main() {\n \n err := gout.POST(\":8080/colorjson\").\n  Debug(debug.NoColor()).\n  SetJSON(gout.H{\"str\": \"foo\",\n   \"num\":   100,\n   \"bool\":  false,\n   \"null\":  nil,\n   \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n   \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n  }).Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n### Custom debug mode\n\ndebug 自定义模式，可传递函数。下面演示用环境变量开启debug模式(只有传递IOS_DEBUG环境变量才输出日志)\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/debug\"\n    \"os\"\n)\n\nfunc IOSDebug() debug.Apply {\n    return gout.DebugFunc(func(o *debug.Options) {\n        if len(os.Getenv(\"IOS_DEBUG\")) \u003e 0 { \n            o.Debug = true\n        }\n    })  \n}\n\nfunc main() {\n\n    s := \"\"\n    err := gout.\n        GET(\"127.0.0.1:8080\").\n        // Debug可以支持自定义方法\n        // 可以实现设置某个环境变量才输出debug信息\n        // 或者debug信息保存到文件里面，可以看下_example/15-debug-save-file.go\n        Debug(IOSDebug()).\n        SetBody(\"test hello\").\n        BindBody(\u0026s).\n        Do()\n\n    fmt.Printf(\"err = %v\\n\", err)\n}\n\n// env IOS_DEBUG=true go run customize.go\n```\n\n[返回](#内容)\n\n### trace info\n\ndebug.Trace()可输出http各个阶段的耗时，比如dns lookup时间，tcp连接时间等等。可以很方便的做些性能调优。\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/debug\"\n)\n\nfunc openDebugTrace() {\n    err := gout.POST(\":8080/colorjson\").\n        Debug(debug.Trace()).\n        SetJSON(gout.H{\"str\": \"foo\",\n            \"num\":   100,\n            \"bool\":  false,\n            \"null\":  nil,\n            \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n            \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n        }).Do()\n\n    if err != nil {\n        fmt.Printf(\"err = %v\\n\", err)\n    }\n}\n\n```\n\n* output\n\n```console\n=================== Trace Info(S): ===================\n     DnsDuration           : 0s\n     ConnDuration          : 868.623µs\n     TLSDuration           : 0s\n     RequestDuration       : 376.712µs\n     WaitResponeDuration   : 717.008µs\n     ResponseDuration      : 76.158µs\n     TotalDuration         : 2.13921ms\n=================== Trace Info(E): ===================\n```\n\n[返回](#内容)\n\n### save to writer\n\n`debug.ToWriter`可以传递任何io.Writer对象，比如`bytes.Buffer`, 文件等。。。\n\n```go\npackage main\n\nimport (\n \"bytes\"\n \"fmt\"\n\n \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/debug\"\n)\n\nfunc main() {\n var buf bytes.Buffer\n err := gout.POST(\":8080/colorjson\").\n  Debug(debug.ToWriter(\u0026buf, false)).\n  SetJSON(gout.H{\"str\": \"foo\",\n   \"num\":   100,\n   \"bool\":  false,\n   \"null\":  nil,\n   \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n   \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n  }).Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n fmt.Println(buf.String())\n}\n\n```\n\n[返回](#内容)\n\n### save to file\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/debug\"\n)\nfunc main() {\n \n err := gout.POST(\":8080/colorjson\").\n  Debug(debug.ToFile(\"./req.txt\", false)).\n  SetJSON(gout.H{\"str\": \"foo\",\n   \"num\":   100,\n   \"bool\":  false,\n   \"null\":  nil,\n   \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n   \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n  }).Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n```\n\n[返回](#内容)\n\n### extracting trace information\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/debug\"\n)\n\nfunc main() {\n    var buf bytes.Buffer\n    err := gout.POST(\":8080/colorjson\").\n        Debug(debug.TraceJSONToWriter(\u0026buf)).\n        SetJSON(gout.H{\"str\": \"foo\",\n            \"num\":   100,\n            \"bool\":  false,\n            \"null\":  nil,\n            \"array\": gout.A{\"foo\", \"bar\", \"baz\"},\n            \"obj\":   gout.H{\"a\": 1, \"b\": 2},\n        }).Do()\n\n    if err != nil {\n        fmt.Printf(\"err = %v\\n\", err)\n    }\n    fmt.Printf(\"%s\", buf.String())\n}\n\n```\n\n[返回](#内容)\n\n## benchmark\n\n### benchmarking a certain number of times\n\n下面的例子，起了20并发。对:8080端口的服务，发送3000次请求进行压测，内容为json结构\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nconst (\n benchNumber     = 30000\n benchConcurrent = 20\n)\n\nfunc main() {\n err := gout.\n  POST(\":8080\").                     //压测本地8080端口\n  SetJSON(gout.H{\"hello\": \"world\"}). //设置请求body内容\n  Filter().                          //打开过滤器\n  Bench().                           //选择bench功能\n  Concurrent(benchConcurrent).       //并发数\n  Number(benchNumber).               //压测次数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n### benchmark-duration\n\n下面的例子，起了20并发。对:8080端口的服务，压测持续时间为10s，内容为json结构\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"time\"\n)\n\nconst (\n benchTime       = 10 * time.Second\n benchConcurrent = 30\n)\n\nfunc main() {\n err := gout.\n  POST(\":8080\").                     //压测本机8080端口\n  SetJSON(gout.H{\"hello\": \"world\"}). //设置请求body内容\n  Filter().                          //打开过滤器\n  Bench().                           //选择bench功能\n  Concurrent(benchConcurrent).       //并发数\n  Durations(benchTime).              //压测时间\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n### benchmark-rate\n\n下面的例子，起了20并发。对:8080端口的服务，压测总次数为3000次，其中每秒发送1000次。内容为json结构\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n)\n\nconst (\n benchNumber     = 3000\n benchConcurrent = 20\n)\n\nfunc main() {\n err := gout.\n  POST(\":8080\").                     //压测本机8080端口\n  SetJSON(gout.H{\"hello\": \"world\"}). //设置请求body内容\n  Filter().                          //打开过滤器\n  Bench().                           //选择bench功能\n  Rate(1000).                        //每秒发1000请求\n  Concurrent(benchConcurrent).       //并发数\n  Number(benchNumber).               //压测次数\n  Do()\n\n if err != nil {\n  fmt.Printf(\"%v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n### Custom benchmark functions\n\n自定义压测函数，构造每次不一样的http request数据\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/google/uuid\"\n    \"github.com/guonaihong/gout\"\n    \"github.com/guonaihong/gout/filter\"\n    \"sync/atomic\"\n)\n\nfunc main() {\n    i := int32(0)\n\n    err := filter.NewBench().\n        Concurrent(30). //开30个go程\n        Number(30000).  //压测30000次\n        Loop(func(c *gout.Context) error {\n\n   // 下面的代码，每次生成不一样的http body 用于压测\n            uid := uuid.New()  //生成uuid\n            id := atomic.AddInt32(\u0026i, 1) //生成id, 可以理解为++i，线程安全版本\n\n            c.POST(\":1234\").SetJSON(gout.H{\"sid\": uid.String(),\n                \"appkey\": fmt.Sprintf(\"ak:%d\", id),\n                \"text\":   fmt.Sprintf(\"test text :%d\", id)})\n            return nil\n\n        }).Do()\n\n    if err != nil {\n        fmt.Printf(\"err = %v\\n\", err)\n    }\n}\n\n```\n\n[返回](#内容)\n\n## retry-backoff\n\nretry 功能使用带抖动功能和指数回退的算法实现[backoff](http://www.awsarchitectureblog.com/2015/03/backoff.html)\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"time\"\n)\n\nfunc main() {\n err := gout.HEAD(\"127.0.0.1:8080\").\n  Debug(true).                      //打开debug模式\n  Filter().                         //打开过滤器\n  Retry().                          //打开重试模式\n  Attempt(5).                       //最多重试5次\n  WaitTime(500 * time.Millisecond). //基本等待时间\n  MaxWaitTime(3 * time.Second).     //最长等待时间\n  Do()\n\n if err != nil {\n  fmt.Printf(\"err = %v\\n\", err)\n }\n}\n\n```\n\n[返回](#内容)\n\n### retry conditions httpcode\n\n指定重试条件，这里面的例子是服务端返回的状态码是209进行重试\n[完整代码](_example/19c-retry-httpcode.go)\n\n``` go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/filter\"\n \"time\"\n)\n\nfunc useRetryFuncCode() {\n s := \"\"\n err := gout.GET(\":8080/code\").Debug(true).BindBody(\u0026s).F().\n  Retry().Attempt(3).WaitTime(time.Millisecond * 10).MaxWaitTime(time.Millisecond * 50).\n  Func(func(c *gout.Context) error {\n   if c.Error != nil || c.Code == 209 {\n    return filter.ErrRetry\n   }\n\n   return nil\n\n  }).Do()\n\n fmt.Printf(\"err = %v\\n\", err)\n}\n```\n\n[返回](#内容)\n\n### retry conditions backupurl\n\n指定条件进行重试，这里的例子是默认url不能访问，使用backup url进行访问\n[完整代码](_example/19b-retry-customize-backup.go)\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"github.com/guonaihong/gout/core\"\n \"github.com/guonaihong/gout/filter\"\n \"time\"\n)\nfunc useRetryFunc() {\n // 获取一个没有服务绑定的端口\n port := core.GetNoPortExists()\n s := \"\"\n\n err := gout.GET(\":\" + port).Debug(true).BindBody(\u0026s).F().\n  Retry().Attempt(3).WaitTime(time.Millisecond * 10).MaxWaitTime(time.Millisecond * 50).\n  Func(func(c *gout.Context) error {\n   if c.Error != nil {\n    c.SetHost(\":1234\") //必须是存在的端口\n    return filter.ErrRetry\n   }\n   return nil\n\n  }).Do()\n fmt.Printf(\"err = %v\\n\", err)\n}\n```\n\n[返回](#内容)\n\n# import\n\n## send raw http request\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n s := `POST /colorjson HTTP/1.1\nHost: 127.0.0.1:8080\nUser-Agent: Go-http-client/1.1\nContent-Length: 97\nContent-Type: application/json\nAccept-Encoding: gzip\n\n{\"array\":[\"foo\",\"bar\",\"baz\"],\"bool\":false,\"null\":null,\"num\":100,\"obj\":{\"a\":1,\"b\":2},\"str\":\"foo\"}\n    `\n err := gout.NewImport().RawText(s).Debug(true).SetHost(\":1234\").Do()\n if err != nil {\n  fmt.Printf(\"err = %s\\n\", err)\n  return\n }\n}\n\n```\n\n[返回](#内容)\n\n# export\n\n## generate curl command\n\n仅仅生成curl命令, 不会发送http请求\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n    // 1.formdata\n    err := gout.GET(\":1234\").\n        SetForm(gout.A{\"text\", \"good\", \"mode\", \"A\", \"voice\", gout.FormFile(\"./t8.go\")}).\n        Export().Curl().Do()\n    // output:\n    // curl -X GET -F \"text=good\" -F \"mode=A\" -F \"voice=@./voice\" \"http://127.0.0.1:1234\"\n\n    // 2.json body\n    err = gout.GET(\":1234\").\n        SetJSON(gout.H{\"key1\": \"val1\", \"key2\": \"val2\"}).\n        Export().Curl().Do()\n    // output:\n    // curl -X GET -H \"Content-Type:application/json\" -d \"{\\\"key1\\\":\\\"val1\\\",\\\"key2\\\":\\\"val2\\\"}\" \"http://127.0.0.1:1234\"\n\n    fmt.Printf(\"%v\\n\", err)\n}\n\n```\n\n[返回](#内容)\n\n## generate curl command and send HTTP request\n\n生成curl命令, 同时执行http请求, 在```Curl()```命令之行跟上```GenAndSend()```接口\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n    // 1.formdata\n    err := gout.GET(\":1234\").\n        SetForm(gout.A{\"text\", \"good\", \"mode\", \"A\", \"voice\", gout.FormFile(\"./t8.go\")}).\n        Export().Curl().GenAndSend().Do()\n    // output:\n    // curl -X GET -F \"text=good\" -F \"mode=A\" -F \"voice=@./voice\" \"http://127.0.0.1:1234\"\n\n    // 2.json body\n    err = gout.GET(\":1234\").\n        SetJSON(gout.H{\"key1\": \"val1\", \"key2\": \"val2\"}).\n        Export().Curl().GenAndSend().Do()\n    // output:\n    // curl -X GET -H \"Content-Type:application/json\" -d \"{\\\"key1\\\":\\\"val1\\\",\\\"key2\\\":\\\"val2\\\"}\" \"http://127.0.0.1:1234\"\n\n    fmt.Printf(\"%v\\n\", err)\n}\n```\n\n# Incoming custom *http.Client\n\n使用New接口即可使用自己的http.Client对象\n\n```go\npackage main\n\nimport (\n \"fmt\"\n \"net/http\"\n\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n\n c := \u0026http.Client{} //http.Client里面有fd连接池，如果对这块优化不是太了解，只使用一个实例就行\n err := gout.New(c). // New接口可传入http.Client对象\n    GET(\"www.qq.com\").\n    Debug(true).\n    Do()\n\n if err != nil {\n  fmt.Printf(\"err = %s\\n\", err)\n  return\n }\n}\n\n```\n\n# Using chunked data format\n\n使用Chunked接口, 设置为\"Transfer-Encoding: chunked\"的数据编码方式\n\n``` go\npackage main\n\nimport (\n        \"fmt\"\n\n        \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n        err := gout.POST(\":8080\").\n                Chunked().\n                SetBody(\"11111111111\").\n                Do()\n        if err != nil {\n                fmt.Printf(\"err :%v\\n\", err)\n        }   \n}\n// 使用nc 起一个tcp服务, 使用上面的代码发起数据观察下结果\n// nc -l 8080\n```\n\n[返回](#内容)\n\n# NewWithOpt\n\n这里记录全局配置的方法, 后面所有的全局配置都推荐使用```gout.NewWithOpt```接口的实现\n\n## insecure skip verify\n\n忽略ssl验证, 使用```gout.WithInsecureSkipVerify()```接口配置该功能, 传入```gout.NewWithOpt```接口即可生效.\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n // globalWithOpt里面包含连接池, 这是一个全局可复用的对象\n globalWithOpt := gout.NewWithOpt(gout.WithInsecureSkipVerify())\n err := globalWithOpt.GET(\"url\").Do()\n if err != nil {\n  fmt.Printf(\"err = %v\\n\" ,err)\n  return\n }\n}\n```\n\n[返回](#内容)\n\n## turn off 3xx status code automatic jump\n\ngolang client库默认遇到301的状态码会自动跳转重新发起新请求, 你希望关闭这种默认形为, 那就使用下面的功能\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n // globalWithOpt里面包含连接池, 这是一个全局可复用的对象, 一个进程里面可能只需创建1个\n globalWithOpt := gout.NewWithOpt(gout.WithClose3xxJump())\n err := globalWithOpt.GET(\"url\").Do()\n if err != nil {\n  fmt.Printf(\"err = %v\\n\" ,err)\n  return\n }\n}\n```\n\n[返回](#内容)\n\n## new with opt set timeout\n\n```gout.WithTimeout``` 为了让大家少用```gout.SetTimeout```而设计\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n // globalWithOpt里面包含连接池, 这是一个全局可复用的对象, 一个进程里面可能只需创建1个\n globalWithOpt := gout.NewWithOpt(gout.WithTimeout())\n err := globalWithOpt.GET(\"url\").Do()\n if err != nil {\n  fmt.Printf(\"err = %v\\n\" ,err)\n  return\n }\n}\n```\n\n[返回](#内容)\n\n## new with opt unix socket\n\n```gout.WithUnixSocket``` 为了让大家少用```.UnixSocket```而设计\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n // globalWithOpt里面包含连接池, 这是一个全局可复用的对象, 一个进程里面可能只需创建1个, 如果有多个不同的unixsocket，可以创建多个\n globalWithOpt := gout.NewWithOpt(gout.WithUnixSocket(\"/tmp/test.socket\"))\n err := globalWithOpt.GET(\"url\").Do()\n if err != nil {\n  fmt.Printf(\"err = %v\\n\" ,err)\n  return\n }\n}\n```\n\n[返回](#内容)\n\n## new with opt proxy\n\n```gout.WithProxy``` 为了让大家少用```.SetProxy```而设计\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n // globalWithOpt里面包含连接池, 这是一个全局可复用的对象, 一个进程里面可能只需创建1个, 如果有多个不同的proxy，可以创建多个\n globalWithOpt := gout.NewWithOpt(gout.WithProxy(\"http://127.0.0.1:7000\"))\n err := globalWithOpt.GET(\"url\").Do()\n if err != nil {\n  fmt.Printf(\"err = %v\\n\" ,err)\n  return\n }\n}\n```\n\n[返回](#内容)\n\n## new with opt socks5\n\n```gout.WithSocks5``` 为了让大家少用```.SetSOCKS5```而设计\n\n```go\nimport (\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n // globalWithOpt里面包含连接池, 这是一个全局可复用的对象, 一个进程里面可能只需创建1个, 如果有多个不同的socks5，可以创建多个\n globalWithOpt := gout.NewWithOpt(gout.WithSocks5(\"127.0.0.1:7000\"))\n err := globalWithOpt.GET(\"url\").Do()\n if err != nil {\n  fmt.Printf(\"err = %v\\n\" ,err)\n  return\n }\n}\n```\n\n[返回](#内容)\n\n# Global configuration\n\n## set timeout\n\n设置全局超时时间。可以简化一些代码。在使用全局配置默认你已经了解它会带来的一些弊端.\n\n```go\n\npackage main\n\nimport (\n \"fmt\"\n \"github.com/guonaihong/gout\"\n \"time\"\n)\n\nfunc main() {\n gout.SetTimeout(time.Second * 1)\n err := gout.GET(\"www.baidu.com\").Do()\n if err != nil {\n  fmt.Printf(\"err is:%v\\n\")\n }\n}\n```\n\n[返回](#内容)\n\n## set debug\n\n打开全局debug开关。\n\n```go\npackage main\n\nimport (\n \"fmt\"\n\n \"github.com/guonaihong/gout\"\n)\n\nfunc main() {\n gout.SetDebug(true)\n err := gout.GET(\":8080/colorjson\").Do()\n if err != nil {\n  fmt.Printf(\"err is:%v\\n\")\n }\n}\n\n```\n\n[返回](#内容)\n\n# Unique features\n\n## forward gin data\n\ngout 设计之初就考虑到要和gin协同工作的可能性，下面展示如何方便地使用gout转发gin绑定的数据。\n\n```go\npackage main\n\nimport (\n \"github.com/gin-gonic/gin\"\n \"github.com/guonaihong/gout\"\n)\n\ntype testQuery struct {\n Size int    `query:\"size\" form:\"size\"` // query tag是gout设置查询字符串需要的\n Page int    `query:\"page\" form:\"page\"`\n Ak   string `query:\"ak\" form:\"ak\"`\n}\n\n//下一个服务节点\nfunc nextSever() {\n r := gin.Default()\n\n r.GET(\"/query\", func(c *gin.Context) {\n  q := testQuery{}\n  err := c.ShouldBindQuery(\u0026q)\n  if err != nil {\n   return\n  }\n  c.JSON(200, q)\n })\n r.Run(\":1234\")\n}\n\nfunc main() {\n go nextSever()\n r := gin.Default()\n\n // 演示把gin绑定到的查询字符串转发到nextServer节点\n r.GET(\"/query\", func(c *gin.Context) {\n  q := testQuery{}\n  // 绑定查询字符串\n  err := c.ShouldBindQuery(\u0026q)\n  if err != nil {\n   return\n  }\n\n  // 开发转发, 复用gin所用结构体变量q\n  code := 0 // http code\n  err := gout.\n   //发起GET请求\n   GET(\"127.0.0.1:1234/query\").\n   //设置查询字符串\n   SetQuery(q).\n   //关心http server返回的状态码 设置该函数\n   Code(\u0026code).\n   Do()\n  if err != nil || code != 200 { /* todo Need to handle errors here */\n  }\n  c.JSON(200, q)\n })\n\n r.Run()\n}\n\n// http client\n// curl '127.0.0.1:8080/query?size=10\u0026page=20\u0026ak=test'\n```\n\n[返回](#内容)\n\n# FAQ\n\n## gout benchmark性能如何\n\n下面是与apache ab的性能对比 [_example/16d-benchmark-vs-ab.go](_example/16d-benchmark-vs-ab.go)\n\n![gout-vs-ab.png](https://raw.githubusercontent.com/guonaihong/images/master/gout/gout-vs-ab.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguonaihong%2Fgout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguonaihong%2Fgout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguonaihong%2Fgout/lists"}