From f0327d0c931a2d343b772d95d3b8c6d104c3165c Mon Sep 17 00:00:00 2001
From: = <428192774@qq.com>
Date: Sat, 10 Dec 2022 02:06:12 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Ebodyentry=E8=A7=A3=E6=9E=90?=
 =?UTF-8?q?=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=8F=AF=E8=87=AA=E5=AE=9A=E4=B9=89?=
 =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=87=BD=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.MD                      |   4 +-
 body.go                        |  37 ++++++++--
 excample/simpleRequest_test.go |   2 +-
 go.mod                         |   2 +-
 headers.go                     |  10 +--
 options.go                     |  17 +++++
 param.go                       |   4 +-
 parser.go                      | 111 ++++++++++++++++++++++++++++++
 simpleRequest.go               | 122 ++++++++++++++++++---------------
 simpleRequest_test.go          |   2 +-
 10 files changed, 232 insertions(+), 79 deletions(-)
 create mode 100644 options.go
 create mode 100644 parser.go

diff --git a/README.MD b/README.MD
index 78f45f6..158af9f 100644
--- a/README.MD
+++ b/README.MD
@@ -90,7 +90,7 @@ r.QueryParams().Set("user", "dorlolo")
 #### 2.4.2 map赋值
 不会覆盖上面之前填充过的参数
 ```go
-pamarsBulid := make(map[string]interface{})
+pamarsBulid := make(map[string]any)
 pamarsBulid["passwd"] = "123456"
 pamarsBulid["action"] = "login"
 r.QueryParams().Sets(pamarsBulid)
@@ -118,7 +118,7 @@ r.Body().Set("beginDate", "2022-03-01").Set("endDate", "2022-03-03")
 
 #### 2.5.2 map赋值
 ```go
-bodyBulid := map[string]interface{}{
+bodyBulid := map[string]any{
     "beginDate":"2022-03-01",
 	"endDate":"2022-03-03",
 }
diff --git a/body.go b/body.go
index b3397f9..cc60aed 100644
--- a/body.go
+++ b/body.go
@@ -7,25 +7,48 @@
 
 package simpleRequest
 
-var (
-	stringBodyType = "__STRING_BODY__"
+type EntryMark string
+
+func (b EntryMark) string() string {
+	return string(b)
+}
+
+const (
+	StringEntryType EntryMark = "__STRING_ENTRY__"
+	BytesEntryType  EntryMark = "__BYTES_ENTRY__"
+	ModelEntryType  EntryMark = "__MODEL_ENTRY__"
+	MapEntryType    EntryMark = "__MAP_ENTRY__"
 )
 
 type BodyConf struct {
 	simpleReq *SimpleRequest
 }
 
-func (s *BodyConf) Set(key string, value interface{}) *BodyConf {
-	s.simpleReq.tempBody[key] = value
+func (s *BodyConf) Set(key string, value any) *BodyConf {
+	s.simpleReq.BodyEntries[key] = value
 	return s
 }
-func (s *BodyConf) Sets(data map[string]interface{}) *BodyConf {
+func (s *BodyConf) Sets(data map[string]any) *BodyConf {
+	s.simpleReq.BodyEntryMark = MapEntryType
 	for k, v := range data {
-		s.simpleReq.tempBody[k] = v
+		s.simpleReq.BodyEntries[k] = v
 	}
 	return s
 }
 func (s *BodyConf) SetString(strData string) *BodyConf {
-	s.simpleReq.tempBody[stringBodyType] = strData
+	s.simpleReq.BodyEntryMark = StringEntryType
+	s.simpleReq.BodyEntries[StringEntryType.string()] = strData
+	return s
+}
+
+func (s *BodyConf) SetBytes(byteData []byte) *BodyConf {
+	s.simpleReq.BodyEntryMark = BytesEntryType
+	s.simpleReq.BodyEntries[BytesEntryType.string()] = byteData
+	return s
+}
+
+func (s *BodyConf) SetModel(model any) *BodyConf {
+	s.simpleReq.BodyEntryMark = ModelEntryType
+	s.simpleReq.BodyEntries[ModelEntryType.string()] = model
 	return s
 }
diff --git a/excample/simpleRequest_test.go b/excample/simpleRequest_test.go
index 101390f..fca8b2d 100644
--- a/excample/simpleRequest_test.go
+++ b/excample/simpleRequest_test.go
@@ -26,7 +26,7 @@ func TestRequest(t *testing.T) {
 	//设置params
 	r.QueryParams().Set("user", "dorlolo")
 	//批量添加,不会覆盖上面user
-	pamarsBulid := map[string]interface{}{
+	pamarsBulid := map[string]any{
 		"passwd": "123456",
 		"action": "login",
 	}
diff --git a/go.mod b/go.mod
index 7fae1aa..9bdd457 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
 module github.com/dorlolo/simpleRequest
 
-go 1.17
+go 1.18
diff --git a/headers.go b/headers.go
index 2dd0247..567ffeb 100644
--- a/headers.go
+++ b/headers.go
@@ -9,7 +9,6 @@ package simpleRequest
 
 import (
 	"bytes"
-	"encoding/json"
 	"math/rand"
 	"net/http"
 	"regexp"
@@ -36,7 +35,7 @@ var (
 
 	jsonCheck = regexp.MustCompile(`(?i:(application|text)/(json|.*\+json|json\-.*)(;|$))`)
 	xmlCheck  = regexp.MustCompile(`(?i:(application|text)/(xml|.*\+xml)(;|$))`)
-	bufPool   = &sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}
+	bufPool   = &sync.Pool{New: func() any { return &bytes.Buffer{} }}
 )
 
 var userAgentList = [...]string{
@@ -101,11 +100,6 @@ func (s *HeadersConf) SetConentType(value string) *HeadersConf {
 }
 
 func (s *HeadersConf) ConentType_json() *HeadersConf {
-	jsonData, err := json.Marshal(s.simpleReq.tempBody)
-	if err == nil {
-		s.simpleReq.body = bytes.NewReader([]byte("{}"))
-	}
-	s.simpleReq.body = bytes.NewReader(jsonData)
 	s.simpleReq.headers.Set(hdrContentTypeKey, jsonContentType)
 	return s
 }
@@ -118,7 +112,7 @@ func (s *HeadersConf) ConentType_charsetUtf8() *HeadersConf {
 func (s *HeadersConf) ConentType_formData() *HeadersConf {
 	//tmp := url.Values{}
 
-	//for k, v := range s.simpleReq.tempBody {
+	//for k, v := range s.simpleReq.BodyEntry {
 	//	tmp.Add(k, fmt.Sprintf("%v", v))
 	//}
 	s.simpleReq.headers.Set(hdrContentTypeKey, formDataType)
diff --git a/options.go b/options.go
new file mode 100644
index 0000000..8a2becc
--- /dev/null
+++ b/options.go
@@ -0,0 +1,17 @@
+// Package simpleRequest -----------------------------
+// @file      : options.go
+// @author    : JJXu
+// @contact   : wavingBear@163.com
+// @time      : 2022/12/10 01:45:37
+// -------------------------------------------
+package simpleRequest
+
+type OPTION func(r *SimpleRequest) *SimpleRequest
+
+//OptionNewBodyEntryParser 新增或覆盖BodyEntryParser
+func OptionNewBodyEntryParser(contentType string, parser IBodyEntryParser) OPTION {
+	return func(r *SimpleRequest) *SimpleRequest {
+		r.bodyEntryParsers[contentType] = parser
+		return r
+	}
+}
diff --git a/param.go b/param.go
index 551a6d2..6e8425f 100644
--- a/param.go
+++ b/param.go
@@ -17,7 +17,7 @@ type QueryParams struct {
 }
 
 //batch settings
-func (s *QueryParams) Sets(data map[string]interface{}) *QueryParams {
+func (s *QueryParams) Sets(data map[string]any) *QueryParams {
 	for k, v := range data {
 		s.simpleReq.queryParams.Set(k, fmt.Sprintf("%v", v))
 	}
@@ -25,7 +25,7 @@ func (s *QueryParams) Sets(data map[string]interface{}) *QueryParams {
 }
 
 //single settings
-func (s *QueryParams) Set(key string, value interface{}) *QueryParams {
+func (s *QueryParams) Set(key string, value any) *QueryParams {
 	s.simpleReq.queryParams.Set(key, fmt.Sprintf("%v", value))
 	return s
 }
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..b35b2a3
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,111 @@
+// Package simpleRequest -----------------------------
+// @file      : parser.go
+// @author    : JJXu
+// @contact   : wavingBear@163.com
+// @time      : 2022/12/10 00:48:45
+// -------------------------------------------
+package simpleRequest
+
+import (
+	"bytes"
+	"encoding/json"
+	"io"
+	"mime/multipart"
+	"strings"
+)
+
+var bodyEntryParsers = map[string]IBodyEntryParser{
+	jsonContentType: new(JsonParser),
+	formDataType:    new(FormDataParser),
+}
+
+type IBodyEntryParser interface {
+	Unmarshal(bodyType EntryMark, BodyEntry map[string]any) io.Reader
+}
+
+type JsonParser struct{}
+
+func (JsonParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any) io.Reader {
+	switch bodyType {
+	case StringEntryType:
+		return strings.NewReader(BodyEntry[StringEntryType.string()].(string))
+	case BytesEntryType:
+		return bytes.NewReader(BodyEntry[BytesEntryType.string()].([]byte))
+	case ModelEntryType:
+		jsonData, err := json.Marshal(BodyEntry)
+		if err == nil {
+			return bytes.NewReader(jsonData)
+		} else {
+			return strings.NewReader("{}")
+		}
+	case MapEntryType:
+		jsonData, err := json.Marshal(BodyEntry)
+		if err == nil {
+			return bytes.NewReader(jsonData)
+		} else {
+			return strings.NewReader("{}")
+		}
+	default:
+		if len(BodyEntry) > 0 {
+			jsonData, err := json.Marshal(BodyEntry)
+			if err == nil {
+				return bytes.NewReader(jsonData)
+			}
+		}
+		return strings.NewReader("{}")
+	}
+}
+
+type FormDataParser struct {
+	ContentType string
+}
+
+func (f *FormDataParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any) (body io.Reader) {
+	switch bodyType {
+	case MapEntryType:
+		body, f.ContentType = multipartCommonParse(BodyEntry)
+	case ModelEntryType:
+		tb := BodyEntry[ModelEntryType.string()]
+		buffer, err := json.Marshal(tb)
+		if err != nil {
+			panic(err.Error())
+		}
+		var mapper map[string]any
+		err = json.Unmarshal(buffer, &mapper)
+		if err != nil {
+			panic(err.Error())
+		}
+		body, f.ContentType = multipartCommonParse(mapper)
+	case StringEntryType:
+		f.ContentType = formDataType
+		return strings.NewReader(BodyEntry[StringEntryType.string()].(string))
+	case BytesEntryType:
+		f.ContentType = formDataType
+		return bytes.NewReader(BodyEntry[StringEntryType.string()].([]byte))
+	default:
+		body, f.ContentType = multipartCommonParse(BodyEntry)
+	}
+	f.ContentType = formDataType
+	return nil
+}
+func multipartCommonParse(BodyEntry map[string]any) (reader io.Reader, contentType string) {
+	body := &bytes.Buffer{}
+	writer := multipart.NewWriter(body)
+	for k, sv := range BodyEntry {
+		switch sv.(type) {
+		case string:
+			strSv, _ := sv.(string)
+			_ = writer.WriteField(k, strSv)
+		case []string:
+			sss, _ := sv.([]string)
+			for _, v := range sss {
+				_ = writer.WriteField(k, v)
+			}
+		}
+	}
+	err := writer.Close()
+	if err != nil {
+		panic(err)
+	}
+	return body, writer.FormDataContentType()
+}
diff --git a/simpleRequest.go b/simpleRequest.go
index 4eab00e..b47b9ef 100644
--- a/simpleRequest.go
+++ b/simpleRequest.go
@@ -8,33 +8,37 @@
 package simpleRequest
 
 import (
-	"bytes"
 	"crypto/tls"
-	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
-	"mime/multipart"
 	"net/http"
 	"net/url"
 	"strings"
 	"time"
 )
 
-func NewRequest() *SimpleRequest {
+func NewRequest(opts ...OPTION) *SimpleRequest {
 	var (
 		hd = http.Header{}
 		qp = url.Values{}
 	)
 
-	return &SimpleRequest{
+	var r = &SimpleRequest{
 		//headers: make(map[string]string),
 		//cookies: make(map[string]string),
-		timeout:     time.Second * 7,
-		queryParams: qp,
-		headers:     hd,
-		tempBody:    make(map[string]interface{}),
+		timeout:          time.Second * 7,
+		queryParams:      qp,
+		headers:          hd,
+		BodyEntries:      make(map[string]any),
+		bodyEntryParsers: bodyEntryParsers,
 	}
+	if len(opts) > 0 {
+		for _, o := range opts {
+			r = o(r)
+		}
+	}
+	return r
 }
 
 type SimpleRequest struct {
@@ -44,13 +48,16 @@ type SimpleRequest struct {
 	headers     http.Header
 	transport   *http.Transport
 
-	tempBody map[string]interface{}
-	timeout  time.Duration
+	BodyEntryMark    EntryMark
+	BodyEntries      map[string]any
+	bodyEntryParsers map[string]IBodyEntryParser
+
+	timeout time.Duration
 
 	Response http.Response //用于获取完整的返回内容。请注意要在请求之后才能获取
 	Request  http.Request  //用于获取完整的请求内容。请注意要在请求之后才能获取
 	//cookies           map[string]string
-	//data              interface{}
+	//data              any
 	//cli               *http.Client
 	//debug             bool
 	//method            string
@@ -62,16 +69,16 @@ type SimpleRequest struct {
 	//checkRedirect     func(req *http.Request, via []*http.Request) error
 }
 
-func (s *SimpleRequest) NewRequest() *SimpleRequest {
-	var qp = url.Values{}
-	return &SimpleRequest{
-		//headers: make(map[string]string),
-		//cookies: make(map[string]string),
-		timeout:     time.Second * 7,
-		queryParams: qp,
-		tempBody:    make(map[string]interface{}),
-	}
-}
+//func (s *SimpleRequest) NewRequest() *SimpleRequest {
+//	var qp = url.Values{}
+//	return &SimpleRequest{
+//		//headers: make(map[string]string),
+//		//cookies: make(map[string]string),
+//		timeout:     time.Second * 7,
+//		queryParams: qp,
+//		BodyEntries:   make(map[string]any),
+//	}
+//}
 
 //------------------------------------------------------
 //
@@ -258,49 +265,50 @@ func (s *SimpleRequest) initBody() {
 	contentTypeData := s.headers.Get(hdrContentTypeKey)
 	switch {
 	case IsJSONType(contentTypeData):
-		jsonData, err := json.Marshal(s.tempBody)
-		if err == nil {
-			s.body = bytes.NewReader(jsonData)
-		} else {
-			s.body = bytes.NewReader([]byte("{}"))
+		var parser, ok = s.bodyEntryParsers[jsonContentType]
+		if !ok {
+			panic(fmt.Sprintf("cannot find %s type parser", contentTypeData))
 		}
-	case strings.Contains(contentTypeData, "multipart/form-data"):
-		body := &bytes.Buffer{}
-		writer := multipart.NewWriter(body)
-		//data := url.Values{}
-		for k, sv := range s.tempBody {
-			switch sv.(type) {
-			case string:
-				strSv, _ := sv.(string)
-				_ = writer.WriteField(k, strSv)
-			case []string:
-				sss, _ := sv.([]string)
-				for _, v := range sss {
-					_ = writer.WriteField(k, v)
-				}
-			}
+		s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
+
+	case strings.Contains(contentTypeData, formDataType):
+		var parser, ok = s.bodyEntryParsers[formDataType]
+		if !ok {
+			panic(fmt.Sprintf("cannot find %s type parser", contentTypeData))
 		}
-		err := writer.Close()
-		if err != nil {
-			panic(err)
-		}
-		s.headers.Set("Content-Type", writer.FormDataContentType())
-		s.body = body
+		s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
+		fdParser := parser.(*FormDataParser)
+		s.headers.Set("Content-Type", fdParser.ContentType)
+
 	case IsXMLType(contentTypeData):
 		//application/soap+xml ,application/xml
-		data, _ := s.tempBody[stringBodyType].(string)
-		s.body = strings.NewReader(data)
+		var parser, ok = s.bodyEntryParsers[xmlDataType]
+		if !ok {
+			data, _ := s.BodyEntries[StringEntryType.string()].(string)
+			s.body = strings.NewReader(data)
+		}
+		s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
+
 	case strings.Contains(contentTypeData, "text") || strings.Contains(contentTypeData, javaScriptType):
-		data, _ := s.tempBody[stringBodyType].(string)
-		s.body = strings.NewReader(data)
+		var parser, ok = s.bodyEntryParsers[textPlainType]
+		if !ok {
+			data, _ := s.BodyEntries[StringEntryType.string()].(string)
+			s.body = strings.NewReader(data)
+		}
+		s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
+
 	case contentTypeData == "" || strings.Contains(contentTypeData, "form-urlencoded"):
 		//默认为x-www-form-urlencoded格式
-		tmpData := url.Values{}
-		for k, v := range s.tempBody {
-			tmpData.Set(k, fmt.Sprintf("%v", v))
+		var parser, ok = s.bodyEntryParsers["form-urlencoded"]
+		if !ok {
+			tmpData := url.Values{}
+			for k, v := range s.BodyEntries {
+				tmpData.Set(k, fmt.Sprintf("%v", v))
+			}
+			s.body = strings.NewReader(tmpData.Encode())
+			s.Headers().ConentType_formUrlencoded()
 		}
-		s.body = strings.NewReader(tmpData.Encode())
-		s.Headers().ConentType_formUrlencoded()
+		s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
 	default:
 		//todo 自动判断数据类型
 		tmpData := url.Values{}
diff --git a/simpleRequest_test.go b/simpleRequest_test.go
index eece469..40648b2 100644
--- a/simpleRequest_test.go
+++ b/simpleRequest_test.go
@@ -57,7 +57,7 @@ func TestPost_withSets(t *testing.T) {
 
 	var r = NewRequest()
 	r.Headers().ConentType_json()
-	r.Body().Sets(map[string]interface{}{
+	r.Body().Sets(map[string]any{
 		"name": "JJXu",
 	})
 	result, err := r.POST("http://localhost:8989/")