mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
Merge pull request #609 from kirileec/master
add Raw* method in ClientResponse to get request and response string
This commit is contained in:
122
net/ghttp/ghttp_client_dump.go
Normal file
122
net/ghttp/ghttp_client_dump.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// dumpTextFormat is the format of the dumped raw string
|
||||
const dumpTextFormat = `+---------------------------------------------+
|
||||
| ghttp %s |
|
||||
+---------------------------------------------+
|
||||
%s
|
||||
%s
|
||||
`
|
||||
|
||||
// ifDumpBody determine whether to output body according to content-type
|
||||
func ifDumpBody(contentType string) bool {
|
||||
// the body should not be output when the body is html or stream.
|
||||
if gstr.Contains(contentType, "application/json") ||
|
||||
gstr.Contains(contentType, "application/xml") ||
|
||||
gstr.Contains(contentType, "multipart/form-data") ||
|
||||
gstr.Contains(contentType, "application/x-www-form-urlencoded") ||
|
||||
gstr.Contains(contentType, "text/plain") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getRequestBody returns the raw text of the request body.
|
||||
func getRequestBody(req *http.Request) string {
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
if !ifDumpBody(contentType) {
|
||||
return ""
|
||||
}
|
||||
// so that the request body can be read again.
|
||||
bodyReader, errGetBody := req.GetBody()
|
||||
if errGetBody != nil {
|
||||
return ""
|
||||
}
|
||||
bytesBody, errReadBody := ioutil.ReadAll(bodyReader)
|
||||
if errReadBody != nil {
|
||||
return ""
|
||||
}
|
||||
return gconv.UnsafeBytesToStr(bytesBody)
|
||||
}
|
||||
|
||||
// getResponseBody returns the text of the response body.
|
||||
func getResponseBody(resp *http.Response) string {
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if !ifDumpBody(contentType) {
|
||||
return ""
|
||||
}
|
||||
bytesBody, errReadBody := ioutil.ReadAll(resp.Body)
|
||||
if errReadBody != nil {
|
||||
return ""
|
||||
}
|
||||
// so that the response body can be read again.
|
||||
resp.Body = ioutil.NopCloser(bytes.NewBuffer(bytesBody))
|
||||
return gconv.UnsafeBytesToStr(bytesBody)
|
||||
}
|
||||
|
||||
// getRequest returns the request related to the response.
|
||||
// will return the copy of request when the request failed.
|
||||
func (r *ClientResponse) getRequest() *http.Request {
|
||||
if r.Response != nil && r.Request != nil {
|
||||
return r.Request
|
||||
}
|
||||
// r.req is the copy of request when the http request failed.
|
||||
if r.req != nil {
|
||||
return r.req
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RawRequest returns the raw text of the request.
|
||||
func (r *ClientResponse) RawRequest() string {
|
||||
// ClientResponse can be nil.
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
req := r.getRequest()
|
||||
if req == nil {
|
||||
return ""
|
||||
}
|
||||
// DumpRequestOut writes more request headers than DumpRequest, such as User-Agent.
|
||||
bs, err := httputil.DumpRequestOut(req, false)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(dumpTextFormat, "REQUEST ", gconv.UnsafeBytesToStr(bs), getRequestBody(req))
|
||||
}
|
||||
|
||||
// RawResponse returns the raw text of the response.
|
||||
func (r *ClientResponse) RawResponse() string {
|
||||
// ClientResponse can be nil.
|
||||
if r == nil || r.Response == nil {
|
||||
return ""
|
||||
}
|
||||
bs, err := httputil.DumpResponse(r.Response, false)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf(dumpTextFormat, "RESPONSE", gconv.UnsafeBytesToStr(bs), getResponseBody(r.Response))
|
||||
}
|
||||
|
||||
// Raw returns the raw text of the request and the response.
|
||||
func (r *ClientResponse) Raw() string {
|
||||
return fmt.Sprintf("%s\n%s", r.RawRequest(), r.RawResponse())
|
||||
}
|
||||
@ -11,10 +11,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
@ -22,6 +18,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
)
|
||||
|
||||
@ -216,21 +217,24 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
|
||||
}
|
||||
// Sending request.
|
||||
var r *http.Response
|
||||
// do not return nil even if the request fails
|
||||
resp = &ClientResponse{}
|
||||
for {
|
||||
if r, err = c.Do(req); err != nil {
|
||||
if c.retryCount > 0 {
|
||||
c.retryCount--
|
||||
time.Sleep(c.retryInterval)
|
||||
} else {
|
||||
return nil, err
|
||||
// we need a copy of the request when the request fails.
|
||||
resp.req = req
|
||||
return resp, err
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
resp = &ClientResponse{
|
||||
Response: r,
|
||||
}
|
||||
resp.Response = r
|
||||
|
||||
// Auto saving cookie content.
|
||||
if c.browserMode {
|
||||
now := time.Now()
|
||||
|
||||
@ -7,13 +7,16 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// ClientResponse is the struct for client request response.
|
||||
type ClientResponse struct {
|
||||
req *http.Request // just a copy of the request when the request failed.
|
||||
*http.Response
|
||||
cookies map[string]string
|
||||
}
|
||||
|
||||
57
net/ghttp/ghttp_unit_client_dump_test.go
Normal file
57
net/ghttp/ghttp_unit_client_dump_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
func Test_Client_Request_13_Dump(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/hello", func(r *ghttp.Request) {
|
||||
r.Response.WriteHeader(200)
|
||||
r.Response.WriteJson(g.Map{"field": "test_for_response_body"})
|
||||
})
|
||||
s.BindHandler("/hello2", func(r *ghttp.Request) {
|
||||
r.Response.WriteHeader(200)
|
||||
r.Response.Writeln(g.Map{"field": "test_for_response_body"})
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
url := fmt.Sprintf("http://127.0.0.1:%d", p)
|
||||
client := ghttp.NewClient().SetPrefix(url).ContentJson()
|
||||
r, err := client.Post("/hello", g.Map{"field": "test_for_request_body"})
|
||||
t.Assert(err, nil)
|
||||
dumpedText := r.RawRequest()
|
||||
t.Assert(gstr.Contains(dumpedText, "test_for_request_body"), true)
|
||||
dumpedText2 := r.RawResponse()
|
||||
t.Assert(gstr.Contains(dumpedText2, "test_for_response_body"), true)
|
||||
|
||||
client2 := ghttp.NewClient().SetPrefix(url).ContentType("text/html")
|
||||
r2, err := client2.Post("/hello2", g.Map{"field": "test_for_request_body"})
|
||||
t.Assert(err, nil)
|
||||
dumpedText3 := r2.RawRequest()
|
||||
t.Assert(gstr.Contains(dumpedText3, "test_for_request_body"), false)
|
||||
dumpedText4 := r2.RawResponse()
|
||||
t.Assert(gstr.Contains(dumpedText4, "test_for_request_body"), false)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user