Compare commits

..

No commits in common. "master" and "v1.1.4" have entirely different histories.

14 changed files with 281 additions and 1003 deletions

206
README.MD
View File

@ -1,53 +1,33 @@
# simpleRequest # simpleRequest
## 1. 说明 ## 1. 说明
[simpleRequest](www.github.com/dorlolo/simpleRequest) 是一款面向对象的http请求库它在Go原生的http库为基础做了一定的封装使开发更加便捷。 [simpleRequest](www.github.com/dorlolo/simpleRequest) 是基于golang原生http库的封装适合用来对第三方接口进行快速地对接和开发。
它具备以下特点:
- 相对于其它请求库,更易于理解和使用。极大减少了开发过程中的代码量和资料查询时间。
- 适合对接一些未遵循restful规范的接口。
> [simpleRequest](www.github.com/dorlolo/simpleRequest) 具备以下特点:
- 没有在代码层面遵循restful规范适合对接不符合规范的接口;
- 轻量级、不需要繁琐的配置;
- 易于理解和使用,减少了资料查询的时间。
这是它的一个示例:
```go
var r = simpleRequest.NewRequest()
//设置请求头
r.Headers().Set("x-token", "d+jfdji*D%1=")
// 添加query
r.QueryParams().Set("user", "JJXu").Set("job", "developer")
//--发送请求
res, err := r.GET("http://www.webSite.com/end/point")
if err != nil {
t.Error(err)
} else {
fmt.Println(res)
}
```
## 2. 如何使用? ## 2. 如何使用?
### 2.2 安装
```bash ### 2.1 模块导入
go get github.com/dorlolo/simpleRequest
```
### 2.2 模块导入
```go ```go
import "github.com/dorlolo/simpleRequest" import "github.com/dorlolo/simpleRequest"
``` ```
### 2.3 实例化 ### 2.2 实例化
```go ```go
var r = simpleRequest.NewRequest() var r = simpleRequest.NewRequest()
``` ```
### 2.4 添加请求头 ### 2.3 添加请求头
#### 2.4.1 单个赋值 #### 2.3.1 单个赋值
```go ```go
r.Headers().Set("token", "d+jfdji*D%1=") r.Headers().Set("token", "d+jfdji*D%1=")
r.Headers().Set("Content-Type", "application/json") r.Headers().Set("Content-Type", "application/json")
``` ```
#### 2.4.2 map赋值 #### 2.3.2 map赋值
```go ```go
mapHeaders:= map[string]string{ mapHeaders:= map[string]string{
"token": "d+jfdji*D%1=", "token": "d+jfdji*D%1=",
@ -56,23 +36,22 @@ mapHeaders:= map[string]string{
r.Headers().Sets(mapHeaders) r.Headers().Sets(mapHeaders)
``` ```
#### 2.4.3 链式赋值 #### 2.3.3 链式赋值
```go ```go
r.Headers().Set("token", "d+jfdji*D%1=").Set("Content-Type", "application/json") r.Headers().Set("token", "d+jfdji*D%1=").Set("Content-Type", "application/json")
``` ```
#### 2.4.4 添加多值 #### 2.3.4 添加多值
对单个key添加多只不要使用`.set`,因为原先的值会被覆盖
```go ```go
// 对相同key的值进行覆盖请使用Set方法
r.Headers().Set("Accept", "text/html") r.Headers().Set("Accept", "text/html")
// 对相同key的值进行追加请使用Add方法
r.Headers().Add("Accept","application/xhtml+xml") r.Headers().Add("Accept","application/xhtml+xml")
r.Headers().Add("Accept","application/xml;q=0.8") r.Headers().Add("Accept","application/xml;q=0.8")
r.Headers().Add("Accept","image/webp")
r.Headers().Add("Accept","*/*;q=0.8") r.Headers().Add("Accept","*/*;q=0.8")
``` ```
#### 2.4.4 使用预设的key #### 2.3.4 使用预设的key
```go ```go
r.Headers().SetConentType("application/json") r.Headers().SetConentType("application/json")
//r.Headers().Set("Content-Type", "application/json") //r.Headers().Set("Content-Type", "application/json")
@ -84,7 +63,7 @@ r.Headers().SetConentEncoding("gzip, deflate, br")
//r.Headers().Set("Content-Encoding", "gzip, deflate, br") //r.Headers().Set("Content-Encoding", "gzip, deflate, br")
``` ```
#### 2.4.5 使用预设的key-value #### 2.3.5 使用预设的key-value
```go ```go
//随机user-agent //随机user-agent
r.Headers().SetRandomUerAgent() r.Headers().SetRandomUerAgent()
@ -103,22 +82,12 @@ r.Headers().ConentType_textPlain()
//r.Headers().Set("Content-Type","text/plain; charset=utf-8") //r.Headers().Set("Content-Type","text/plain; charset=utf-8")
``` ```
#### 2.4.5 忽略指定请求头/禁用默认content-Type ### 2.4 添加queryParams
在默认情况下,`Content-Type`将被自动添加到请求头中,值为`application/x-www-form-urlencoded`。但对于某些场景来说这会导致请求失败。使用下面的方法可以忽略指定的请求头 #### 2.4.1 单个赋值
```go
r.Headers().Omit("Content-Type")
```
或者使用以下方法禁用**默认content-Type**。禁用后当你不主动设置`Content-Type`时,请求头中将不会包含`Content-Type`。
```go
var r = simpleRequest.NewRequest(simpleRequest.OptionDisableDefaultContentType())
````
### 2.5 添加queryParams
#### 2.5.1 单个赋值
```go ```go
r.QueryParams().Set("user", "dorlolo") r.QueryParams().Set("user", "dorlolo")
``` ```
#### 2.5.2 map赋值 #### 2.4.2 map赋值
不会覆盖上面之前填充过的参数 不会覆盖上面之前填充过的参数
```go ```go
pamarsBulid := make(map[string]any) pamarsBulid := make(map[string]any)
@ -127,28 +96,28 @@ pamarsBulid["action"] = "login"
r.QueryParams().Sets(pamarsBulid) r.QueryParams().Sets(pamarsBulid)
``` ```
#### 2.5.3 链式赋值 #### 2.4.3 链式赋值
```go ```go
r.QueryParams().Set("user", "dorlolo").Set("passwd","123456") r.QueryParams().Set("user", "dorlolo").Set("passwd","123456")
``` ```
#### 2.5.4 获取url.Values对象进行赋值 #### 2.4.4 获取url.Values对象进行赋值
对象类型为`*url.Values`,取到地址后,可以使用`url.Values`中的方法继续进行赋值 对象类型为`*url.Values`,取到地址后,可以使用`url.Values`中的方法继续进行赋值
```go ```go
qpData:=r.QueryParams().Gets() qpData:=r.QueryParams().Gets()
qpData.Add("age","18") qpData.Add("age","18")
``` ```
### 2.6 添加请求体body ### 2.5 添加请求体body
#### 2.6.1 单个参数赋值 #### 2.5.1 单个参数赋值
支持和map赋值同时使用 支持和map赋值同时使用
```go ```go
r.Body().Set("beginDate", "2022-03-01").Set("endDate", "2022-03-03") r.Body().Set("beginDate", "2022-03-01").Set("endDate", "2022-03-03")
``` ```
#### 2.6.2 map赋值 #### 2.5.2 map赋值
支持和单个参数赋值同时使用 支持和单个参数赋值同时使用
```go ```go
bodyBulid := map[string]any{ bodyBulid := map[string]any{
@ -158,26 +127,26 @@ bodyBulid := map[string]any{
r.Body().Sets(bodyBulid) r.Body().Sets(bodyBulid)
``` ```
#### 2.6.3 技巧:链式赋值 #### 2.5.3 技巧:链式赋值
```go ```go
r.Body().Set("beginDate", "2022-03-01").Set("endDate", "2022-03-03") r.Body().Set("beginDate", "2022-03-01").Set("endDate", "2022-03-03")
``` ```
#### 2.6.4 字符串赋值 #### 2.5.4 字符串赋值
此方法为一次性赋值,不支持和其它赋值方法同时使用 此方法为一次性赋值,不支持和其它赋值方法同时使用
```go ```go
bodydata:=`{"devSn":"230000000008","type":"day"}` bodydata:=`{"devSn":"230000000008","type":"day"}`
r.Body().SetString(bodydata) r.Body().SetString(bodydata)
``` ```
#### 2.6.5 字节赋值 #### 2.5.5 字节赋值
此方法为一次性赋值,不支持和其它赋值方法同时使用 此方法为一次性赋值,不支持和其它赋值方法同时使用
```go ```go
bytesdata:=[]byte(`{"devSn":"230000000008","type":"day"}`) bytesdata:=[]byte(`{"devSn":"230000000008","type":"day"}`)
r.Body().SetBytes(bytesdata) r.Body().SetBytes(bytesdata)
``` ```
#### 2.6.6 结构体赋值 #### 2.5.6 结构体赋值
此方法为一次性赋值,不支持和其它赋值方法同时使用 此方法为一次性赋值,不支持和其它赋值方法同时使用
```go ```go
type demo struct{ type demo struct{
@ -191,114 +160,38 @@ modeldata:=demo{
r.Body().SetModel(&modeldata) r.Body().SetModel(&modeldata)
``` ```
### 2.7 文件上传与转发 ### 2.6 其它请求参数
### 2.7.1 文件上传
```go
var req = sRequest.NewRequest()
req.Headers().ConentType_formData()
req.Body().
SetFromDataFile("file", "C:\\Users\\lenovo\\Pictures\\Saved Pictures\\demo.jpg").
Set("fromFormat", "jpg").
Set("toFormat", "png")
req.TimeOut(15 * time.Second)
resp, err := req.POST("http://xxx/xxx")
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(string(resp))
```
### 2.7.2 文件转发
下面示例中使用gin作为服务端配合simpleRequest对上传的文件进行转发
1. 通过multipart.FileHeader对象进行转发
```go
func FileForwardUseMultipartFile(c *gin.Context){
file,err:=c.FormFile("file")
var req = sRequest.NewRequest() #### 2.6.1 设置超时时间
req.Headers().ConentType_formData()
req.Body().
SetFromDataMultipartFile("file", file).
Set("fromFormat", "jpg").
Set("toFormat", "png")
req.TimeOut(15 * time.Second)
resp, err := req.POST("http://xxx/xxx")
if err != nil {
fmt.Println(err.Error())
return
}
// parse response and so on
// ...
// ...
```
2. 在一些小众场景下可能已经在外部构建好了body此时也可将body转为bytes传入simpleRequest进行请求
```go
func FileForwardUseBytesBody(c *gin.Context){
file,err:=c.FormFile("file")
// body data prepare
vars (
body = &bytes.Buffer{}
writer = multipart.NewWriter(body)
)
// add file object
filePart, _ := writer.CreateFormFile("file", file.Filename)
src, err := file.Open()
if err != nil {
fmt.Println( err.Error())
return
}
defer src.Close()
_, err = io.Copy(filePart, src)
if err != nil {
fmt.Println(err.Error())
return
}
// add other form data
writer.WriteField("fromFormat", "jpg")
writer.WriteField("toFormat","png")
// post request
_ = writer.close()
var r = simpleRequest.NewRequest()
req.Headers().SetConentType(writer.FormDataContentType())
req.Body().SetBytes(body.Bytes())
req.TimeOut(15 * time.Second)
resp, err := req.POST("http://xxx/xxx")
if err != nil {
fmt.Println(err.Error())
return
}
// parse response and so on
// ...
// ...
}
```
### 2.8 其它请求参数
#### 2.8.1 设置超时时间
```go ```go
r.TimeOut(time.Second * 30) r.TimeOut(time.Second * 30)
``` ```
#### 2.7.2 跳过证书验证 #### 2.6.2 跳过证书验证
```go ```go
r.SkipCertVerify() r.SkipCertVerify()
``` ```
### 2.9 发送请求 ### 2.7 发送请求
#### 2.9.1 post请求 #### 2.7.1 post请求
```go ```go
res, err :=r.POST("https://127.0.0.1:80/excample") res, err :=r.Post("https://127.0.0.1:80/excample")
if err != nil { if err != nil {
ftm.Println( "error occured", err) ftm.Println( "error occured", err)
} else { } else {
fmt.Println(res) fmt.Println(res)
} }
``` ```
#### 2.7.2 发送请求
```go
res, err :=r.Get("https://127.0.0.1:80/excample")
if err != nil {
ftm.Println( "error occured", err)
} else {
fmt.Println(res)
}
```
**支持的请求类型有** **支持的请求类型有**
- POST - POST
- GET - GET
@ -311,17 +204,14 @@ if err != nil {
- TRACE - TRACE
### 2.10 获取上下文 ### 2.8 获取上下文
请注意,需要完成请求后才能获得上下文数据! 请注意,需要完成请求后才能获得上下文数据!
#### 2.10.1 获取请求的上下文对象 #### 2.8.1 获取请求的上下文对象
```go ```go
requestContext:=r.Request requestContext:=r.Request
``` ```
为了让用户能够便于分析调试在进行http请求后r.Request.Body中的数据仍旧可读但是会丧失部分性能如果要禁用此功能请使用以下方法。
```go #### 2.8.2 获取返回的上下文对象
var r = simpleRequest.NewRequest(simpleRequest.OptionDisableDefaultContentType())
```
#### 2.10.2 获取返回的上下文对象
```go ```go
responseContext:=r.Response responseContext:=r.Response
``` ```

View File

@ -1,8 +1,8 @@
/* /*
*FileName: auth.go * @FileName: auth.go
*Author: JJXu * @Author: JJXu
*CreateTime: 2022/3/24 上午12:09 * @CreateTime: 2022/3/24 上午12:09
*Description: * @Description:
*/ */
package simpleRequest package simpleRequest
@ -16,20 +16,24 @@ type Authorization struct {
simpleReq *SimpleRequest simpleReq *SimpleRequest
} }
// Basic //
// Description: 身份验证使用bearer 令牌bearer 令牌 // Basic
// receiver s // @Description: 身份验证使用bearer 令牌bearer 令牌
// param username // @receiver s
// param password // @param username
// @param password
//
func (s *Authorization) Bearer(token string) { func (s *Authorization) Bearer(token string) {
s.simpleReq.headers.Set("Authorization", fmt.Sprintf("Bearer %v", token)) s.simpleReq.headers.Set("Authorization", fmt.Sprintf("Bearer %v", token))
} }
// Basic //
// Description: 身份验证的基本验证方案 // Basic
// receiver s // @Description: 身份验证的基本验证方案
// param username // @receiver s
// param password // @param username
// @param password
//
func (s *Authorization) Basic(username, password string) { func (s *Authorization) Basic(username, password string) {
authStr := fmt.Sprintf("%v:%v", username, password) authStr := fmt.Sprintf("%v:%v", username, password)
data := base64.StdEncoding.EncodeToString([]byte(authStr)) data := base64.StdEncoding.EncodeToString([]byte(authStr))

60
body.go
View File

@ -1,19 +1,12 @@
/* /*
*FileName: body.go * @FileName: body.go
*Author: JJXu * @Author: JJXu
*CreateTime: 2022/3/2 上午1:23 * @CreateTime: 2022/3/2 上午1:23
*Description: * @Description:
*/ */
package simpleRequest package simpleRequest
import (
"bytes"
"io"
"mime/multipart"
"strings"
)
// EntryMark 请求体条目标记用于标记输入的body内容格式 // EntryMark 请求体条目标记用于标记输入的body内容格式
type EntryMark string type EntryMark string
@ -22,29 +15,12 @@ func (b EntryMark) string() string {
} }
const ( const (
StringEntryType EntryMark = "__STRING_ENTRY__" StringEntryType EntryMark = "__STRING_ENTRY__"
BytesEntryType EntryMark = "__BYTES_ENTRY__" BytesEntryType EntryMark = "__BYTES_ENTRY__"
ModelEntryType EntryMark = "__MODEL_ENTRY__" ModelEntryType EntryMark = "__MODEL_ENTRY__"
MapEntryType EntryMark = "__MAP_ENTRY__" MapEntryType EntryMark = "__MAP_ENTRY__"
MultipartEntryType EntryMark = "__MULTIPART_ENTRY__"
FormFilePathKey EntryMark = "__FORM_FILE_PATH_KEY__"
) )
func GetStringEntryTypeBody(bodyEntries map[string]any) io.Reader {
data, ok := bodyEntries[StringEntryType.string()]
if !ok || data == nil {
return nil
}
return strings.NewReader(data.(string))
}
func GetBytesEntryTypeBody(bodyEntries map[string]any) io.Reader {
data, ok := bodyEntries[BytesEntryType.string()]
if !ok || data == "" {
return nil
}
return bytes.NewReader(data.([]byte))
}
type BodyConf struct { type BodyConf struct {
simpleReq *SimpleRequest simpleReq *SimpleRequest
} }
@ -77,23 +53,3 @@ func (s *BodyConf) SetModel(model any) *BodyConf {
s.simpleReq.BodyEntries[ModelEntryType.string()] = model s.simpleReq.BodyEntries[ModelEntryType.string()] = model
return s return s
} }
// 添加上传文件
func (s *BodyConf) SetFromDataFile(key, filePath string) *BodyConf {
s.simpleReq.BodyEntryMark = MultipartEntryType
s.simpleReq.BodyEntries[FormFilePathKey.string()+key] = filePath
if s.simpleReq.headers.Get(hdrContentTypeKey) == "" {
s.simpleReq.headers.Set(hdrContentTypeKey, formDataType)
}
return s
}
// 添加文件适用于服务端文件转发场景比如直接从c.FormFile("file")中获取FileHeader对象转发即可
func (s *BodyConf) SetFromDataMultipartFile(key string, multFile *multipart.FileHeader) *BodyConf {
s.simpleReq.BodyEntryMark = MultipartEntryType
s.simpleReq.BodyEntries[key] = multFile
if s.simpleReq.headers.Get(hdrContentTypeKey) == "" {
s.simpleReq.headers.Set(hdrContentTypeKey, formDataType)
}
return s
}

View File

@ -1,8 +1,8 @@
/* /*
*FileName: simpleRequest_test.go * @FileName: simpleRequest_test.go
*Author: JJXu * @Author: JJXu
*CreateTime: 2022/3/3 下午11:34 * @CreateTime: 2022/3/3 下午11:34
*Description: * @Description:
*/ */
package excample package excample
@ -11,7 +11,6 @@ import (
"fmt" "fmt"
"github.com/dorlolo/simpleRequest" "github.com/dorlolo/simpleRequest"
"net/http" "net/http"
"os"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -50,8 +49,8 @@ func TestRequest(t *testing.T) {
} }
// 测试content-type 为 multipart/form-data格式的数据请求 //测试content-type 为 multipart/form-data格式的数据请求
func TestAuth_formData(t *testing.T) { func TestAuth_fotmData(t *testing.T) {
req := simpleRequest.NewRequest() req := simpleRequest.NewRequest()
req.Headers().ConentType_formData() req.Headers().ConentType_formData()
req.Headers().SetRandomUerAgent() req.Headers().SetRandomUerAgent()
@ -68,7 +67,7 @@ func TestAuth_formData(t *testing.T) {
t.Log(string(data)) t.Log(string(data))
} }
// 测试令牌验证 //测试令牌验证
func TestAuthorization(t *testing.T) { func TestAuthorization(t *testing.T) {
req := simpleRequest.NewRequest() req := simpleRequest.NewRequest()
req.Authorization().Bearer("19f0591e-fab1-4447-90c3-1c60aef78fbd") req.Authorization().Bearer("19f0591e-fab1-4447-90c3-1c60aef78fbd")
@ -133,24 +132,3 @@ func TestTextPlain(t *testing.T) {
} }
} }
// 阿里云Oss文件上传示例
func TestUploadFileToOss(t *testing.T) {
var signedUrl = "" //STS授权url
var xOssCallback = "" //回调信息
var req = simpleRequest.NewRequest()
req.Headers().
Sets(map[string]string{
"X-Oss-Callback": xOssCallback,
})
fileData, err := os.ReadFile("./CHANGELOG.MD")
req.Body().SetBytes(fileData)
body, err := req.PUT(signedUrl)
if err != nil {
t.Error(err)
return
}
fmt.Println(string(body))
}

View File

@ -1,3 +1,10 @@
/*
* @FileName: header.go
* @Author: JJXu
* @CreateTime: 2022/3/1 下午9:44
* @Description:
*/
package simpleRequest package simpleRequest
import ( import (
@ -44,7 +51,7 @@ type HeadersConf struct {
//------------------------------------------------------------- //-------------------------------------------------------------
// Common key settings // Common key settings
// batch settings //batch settings
func (s *HeadersConf) Sets(headers map[string]string) *HeadersConf { func (s *HeadersConf) Sets(headers map[string]string) *HeadersConf {
for k, v := range headers { for k, v := range headers {
s.simpleReq.headers.Set(k, v) s.simpleReq.headers.Set(k, v)
@ -52,7 +59,7 @@ func (s *HeadersConf) Sets(headers map[string]string) *HeadersConf {
return s return s
} }
// single setting //single setting
func (s *HeadersConf) Set(header, value string) *HeadersConf { func (s *HeadersConf) Set(header, value string) *HeadersConf {
s.simpleReq.headers.Set(header, value) s.simpleReq.headers.Set(header, value)
return s return s
@ -63,13 +70,7 @@ func (s *HeadersConf) Add(header, value string) *HeadersConf {
return s return s
} }
// Omit Use to disable automatically generated request headers some like Content_Type. //一般用不到
func (s *HeadersConf) Omit(keys ...string) *HeadersConf {
s.simpleReq.omitHeaderKeys = append(s.simpleReq.omitHeaderKeys, keys...)
return s
}
//deprecated
//func (s *HeadersConf) Values(keys string) *HeadersConf { //func (s *HeadersConf) Values(keys string) *HeadersConf {
// s.simpleReq.headers.Values(keys) // s.simpleReq.headers.Values(keys)
// return s // return s
@ -86,13 +87,13 @@ func (s *HeadersConf) Omit(keys ...string) *HeadersConf {
//------------------------------------------------------------- //-------------------------------------------------------------
// base Key settings // base Key settings
// SetUserAgent //SetUserAgent
func (s *HeadersConf) SetUserAgent(value string) *HeadersConf { func (s *HeadersConf) SetUserAgent(value string) *HeadersConf {
s.simpleReq.headers.Set(hdrUserAgentKey, value) s.simpleReq.headers.Set(hdrUserAgentKey, value)
return s return s
} }
// SetConentType //SetConentType
func (s *HeadersConf) SetConentType(value string) *HeadersConf { func (s *HeadersConf) SetConentType(value string) *HeadersConf {
s.simpleReq.headers.Set(hdrContentTypeKey, value) s.simpleReq.headers.Set(hdrContentTypeKey, value)
return s return s
@ -109,6 +110,11 @@ func (s *HeadersConf) ConentType_charsetUtf8() *HeadersConf {
} }
func (s *HeadersConf) ConentType_formData() *HeadersConf { func (s *HeadersConf) ConentType_formData() *HeadersConf {
//tmp := url.Values{}
//for k, v := range s.simpleReq.BodyEntry {
// tmp.Add(k, fmt.Sprintf("%v", v))
//}
s.simpleReq.headers.Set(hdrContentTypeKey, formDataType) s.simpleReq.headers.Set(hdrContentTypeKey, formDataType)
return s return s
} }
@ -121,6 +127,7 @@ func (s *HeadersConf) ConentType_textPlain() *HeadersConf {
return s return s
} }
//
func (s *HeadersConf) SetConentLength(value string) *HeadersConf { func (s *HeadersConf) SetConentLength(value string) *HeadersConf {
s.simpleReq.headers.Set(hdrContentLengthKey, value) s.simpleReq.headers.Set(hdrContentLengthKey, value)
return s return s
@ -134,22 +141,22 @@ func (s *HeadersConf) SetConentLocation(value string) *HeadersConf {
return s return s
} }
// ------------------------------------------------------------- //-------------------------------------------------------------
// Extended settings // Extended settings
// 随机请求头的User-Agent参数 //随机请求头的User-Agent参数
func (s *HeadersConf) getRandomUerAgent() string { func (s *HeadersConf) getRandomUerAgent() string {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
index := rand.Intn(len(userAgentList)) index := rand.Intn(len(userAgentList))
return userAgentList[index] return userAgentList[index]
} }
// 设置为随机 User-Agent //设置为随机 User-Agent
func (s *HeadersConf) SetRandomUerAgent() *HeadersConf { func (s *HeadersConf) SetRandomUerAgent() *HeadersConf {
s.simpleReq.headers.Set(hdrUserAgentKey, s.getRandomUerAgent()) s.simpleReq.headers.Set(hdrUserAgentKey, s.getRandomUerAgent())
return s return s
} }
// set Authorization //set Authorization
func (s *HeadersConf) SetAuthorization(value string) *HeadersConf { func (s *HeadersConf) SetAuthorization(value string) *HeadersConf {
s.simpleReq.headers.Set("Authorization", value) s.simpleReq.headers.Set("Authorization", value)
return s return s

View File

@ -1,34 +1,17 @@
// Package simpleRequest ----------------------------- // Package simpleRequest -----------------------------
// file : options.go // @file : options.go
// author : JJXu // @author : JJXu
// contact : wavingBear@163.com // @contact : wavingBear@163.com
// time : 2022/12/10 01:45:37 // @time : 2022/12/10 01:45:37
// ------------------------------------------- // -------------------------------------------
package simpleRequest package simpleRequest
type OPTION func(r *SimpleRequest) *SimpleRequest type OPTION func(r *SimpleRequest) *SimpleRequest
// OptionNewBodyEntryParser 新增或覆盖BodyEntryParser //OptionNewBodyEntryParser 新增或覆盖BodyEntryParser
func OptionNewBodyEntryParser(contentType string, parser IBodyEntryParser) OPTION { func OptionNewBodyEntryParser(contentType string, parser IBodyEntryParser) OPTION {
return func(r *SimpleRequest) *SimpleRequest { return func(r *SimpleRequest) *SimpleRequest {
r.bodyEntryParsers[contentType] = parser r.bodyEntryParsers[contentType] = parser
return r return r
} }
} }
// OptionDisableDefaultContentType 禁用默认的ContentType
// 当未指定ContentType时将不会使用默认的ContentType
func OptionDisableDefaultContentType() OPTION {
return func(r *SimpleRequest) *SimpleRequest {
r.disableDefaultContentType = true
return r
}
}
// OptionDisableCopyRequestBody 禁用复制RequestBody
func OptionDisableCopyRequestBody() OPTION {
return func(r *SimpleRequest) *SimpleRequest {
r.disableCopyRequestBody = true
return r
}
}

View File

@ -1,49 +0,0 @@
// Package simpleRequest -----------------------------
// @file : options_test.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2024/1/12 11:27
// -------------------------------------------
package simpleRequest
import (
"io"
"testing"
)
func TestOptionDisableDefaultContentType(t *testing.T) {
go httpserver()
var r = NewRequest(OptionDisableDefaultContentType())
r.Headers()
bodyData := "{'a'=123,'b'=56}"
r.Body().SetString(bodyData)
_, err := r.POST("http://localhost:8989")
if err != nil {
t.Error(err)
return
}
if r.Request.Header.Get(hdrContentTypeKey) != "" {
t.Errorf("query params want '%s' but get '%s'", "", r.Request.Header.Get(hdrContentTypeKey))
}
}
func TestOptionOptionDisableCopyRequestBody(t *testing.T) {
go httpserver()
var r = NewRequest(OptionDisableCopyRequestBody())
r.Headers()
bodyData := "{'a'=123,'b'=56}"
r.Body().SetString(bodyData)
_, err := r.POST("http://localhost:8989")
if err != nil {
t.Error(err)
return
}
body, err := io.ReadAll(r.Request.Body)
if err != nil {
t.Error(err)
return
}
if string(body) != "" {
t.Errorf("query params want '%s' but get '%s'", "", body)
}
}

View File

@ -1,8 +1,8 @@
/* /*
*FileName: param.go * @FileName: param.go
*Author: JJXu * @Author: JJXu
*CreateTime: 2022/3/1 下午9:07 * @CreateTime: 2022/3/1 下午9:07
*Description: * @Description:
*/ */
package simpleRequest package simpleRequest
@ -16,7 +16,7 @@ type QueryParams struct {
simpleReq *SimpleRequest simpleReq *SimpleRequest
} }
// batch settings //batch settings
func (s *QueryParams) Sets(data map[string]any) *QueryParams { func (s *QueryParams) Sets(data map[string]any) *QueryParams {
for k, v := range data { for k, v := range data {
s.simpleReq.queryParams.Set(k, fmt.Sprintf("%v", v)) s.simpleReq.queryParams.Set(k, fmt.Sprintf("%v", v))
@ -24,13 +24,13 @@ func (s *QueryParams) Sets(data map[string]any) *QueryParams {
return s return s
} }
// single settings //single settings
func (s *QueryParams) Set(key string, value any) *QueryParams { func (s *QueryParams) Set(key string, value any) *QueryParams {
s.simpleReq.queryParams.Set(key, fmt.Sprintf("%v", value)) s.simpleReq.queryParams.Set(key, fmt.Sprintf("%v", value))
return s return s
} }
// get all queryParams //get all queryParams
func (s *QueryParams) Gets() *url.Values { func (s *QueryParams) Gets() *url.Values {
return &s.simpleReq.queryParams return &s.simpleReq.queryParams
} }

133
parser.go
View File

@ -1,29 +1,22 @@
// Package sRequest ----------------------------- // Package simpleRequest -----------------------------
// file : parser.go // @file : parser.go
// author : JJXu // @author : JJXu
// contact : wavingBear@163.com // @contact : wavingBear@163.com
// time : 2022/12/10 00:48:45 // @time : 2022/12/10 00:48:45
// ------------------------------------------- // -------------------------------------------
package simpleRequest package simpleRequest
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"encoding/xml"
"fmt"
"io" "io"
"mime/multipart" "mime/multipart"
"net/url"
"os"
"path/filepath"
"strings" "strings"
) )
// 通用类型解析器
var bodyEntryParsers = map[string]IBodyEntryParser{ var bodyEntryParsers = map[string]IBodyEntryParser{
jsonContentType: new(JsonParser), jsonContentType: new(JsonParser),
formDataType: new(FormDataParser), formDataType: new(FormDataParser),
xmlDataType: new(XmlParser),
} }
type IBodyEntryParser interface { type IBodyEntryParser interface {
@ -35,9 +28,9 @@ type JsonParser struct{}
func (JsonParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any) io.Reader { func (JsonParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any) io.Reader {
switch bodyType { switch bodyType {
case StringEntryType: case StringEntryType:
return GetStringEntryTypeBody(BodyEntry) return strings.NewReader(BodyEntry[StringEntryType.string()].(string))
case BytesEntryType: case BytesEntryType:
return GetBytesEntryTypeBody(BodyEntry) return bytes.NewReader(BodyEntry[BytesEntryType.string()].([]byte))
case ModelEntryType: case ModelEntryType:
jsonData, err := json.Marshal(BodyEntry[ModelEntryType.string()]) jsonData, err := json.Marshal(BodyEntry[ModelEntryType.string()])
if err == nil { if err == nil {
@ -83,113 +76,35 @@ func (f *FormDataParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any)
} }
body, f.ContentType = multipartCommonParse(mapper) body, f.ContentType = multipartCommonParse(mapper)
case StringEntryType: case StringEntryType:
return GetStringEntryTypeBody(BodyEntry) f.ContentType = formDataType
return strings.NewReader(BodyEntry[StringEntryType.string()].(string))
case BytesEntryType: case BytesEntryType:
return GetBytesEntryTypeBody(BodyEntry) f.ContentType = formDataType
return bytes.NewReader(BodyEntry[BytesEntryType.string()].([]byte))
default: default:
body, f.ContentType = multipartCommonParse(BodyEntry) body, f.ContentType = multipartCommonParse(BodyEntry)
} }
return f.ContentType = formDataType
return nil
} }
func multipartCommonParse(BodyEntry map[string]any) (reader io.Reader, contentType string) { func multipartCommonParse(BodyEntry map[string]any) (reader io.Reader, contentType string) {
body := &bytes.Buffer{} body := &bytes.Buffer{}
formWriter := multipart.NewWriter(body) writer := multipart.NewWriter(body)
for k, sv := range BodyEntry { for k, sv := range BodyEntry {
if strings.Contains(k, FormFilePathKey.string()) { switch sv.(type) {
fieldName := k[len(FormFilePathKey):] case string:
fp := sv.(string) strSv, _ := sv.(string)
filename := filepath.Base(fp) _ = writer.WriteField(k, strSv)
//way1 case []string:
filePart, _ := formWriter.CreateFormFile(fieldName, filename) sss, _ := sv.([]string)
content, err := os.ReadFile(fp) for _, v := range sss {
if err != nil { _ = writer.WriteField(k, v)
panic(err)
}
_, _ = filePart.Write(content)
} else {
switch multValue := sv.(type) {
case string:
_ = formWriter.WriteField(k, multValue)
case []string:
sss, _ := sv.([]string)
for _, v := range sss {
_ = formWriter.WriteField(k, v)
}
case *multipart.FileHeader:
filePart, _ := formWriter.CreateFormFile(k, multValue.Filename)
src, err := multValue.Open()
if err != nil {
panic(err)
return
}
defer src.Close()
_, err = io.Copy(filePart, src)
if err != nil {
panic(err)
return
}
case []byte:
formWriter.WriteField(k, string(multValue))
case int:
formWriter.WriteField(k, fmt.Sprintf("%v", multValue))
} }
} }
} }
err := formWriter.Close() err := writer.Close()
if err != nil { if err != nil {
panic(err) panic(err)
} }
return body, formWriter.FormDataContentType() return body, writer.FormDataContentType()
}
type XmlParser struct{}
func (f XmlParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any) (body io.Reader) {
switch bodyType {
case MapEntryType:
xmlData, err := xml.Marshal(BodyEntry[bodyType.string()])
if err == nil {
return bytes.NewReader(xmlData)
} else {
return strings.NewReader("")
}
case ModelEntryType:
xmlData, err := xml.Marshal(BodyEntry[bodyType.string()])
if err == nil {
return bytes.NewReader(xmlData)
} else {
return strings.NewReader("")
}
case StringEntryType:
return GetStringEntryTypeBody(BodyEntry)
case BytesEntryType:
return GetBytesEntryTypeBody(BodyEntry)
default:
return strings.NewReader("")
}
}
type CommonParser struct {
}
func (f CommonParser) Unmarshal(bodyType EntryMark, BodyEntry map[string]any) (body io.Reader) {
tmpData := url.Values{}
for k, v := range BodyEntry {
switch k {
case StringEntryType.string():
body = GetStringEntryTypeBody(BodyEntry)
break
case BytesEntryType.string():
body = GetBytesEntryTypeBody(BodyEntry)
break
default:
if strings.HasPrefix(k, FormFilePathKey.string()) {
k = k[len(FormFilePathKey):]
}
tmpData.Set(k, fmt.Sprintf("%v", v))
}
}
body = strings.NewReader(tmpData.Encode())
return
} }

View File

@ -1,118 +0,0 @@
// Package simpleRequest -----------------------------
// @file : parser_test.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2024/1/11 18:35
// -------------------------------------------
package simpleRequest
import (
"io"
"reflect"
"testing"
)
func TestFormDataParser_Unmarshal(t *testing.T) {
type fields struct {
ContentType string
}
type args struct {
bodyType EntryMark
BodyEntry map[string]any
}
tests := []struct {
name string
fields fields
args args
wantBody io.Reader
}{
{
name: "StringEntryType",
fields: fields{},
args: args{
bodyType: StringEntryType,
BodyEntry: map[string]any{StringEntryType.string(): "test"},
},
wantBody: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &FormDataParser{
ContentType: tt.fields.ContentType,
}
if gotBody := f.Unmarshal(tt.args.bodyType, tt.args.BodyEntry); !reflect.DeepEqual(gotBody, tt.wantBody) {
t.Errorf("Unmarshal() = %v, want %v", gotBody, tt.wantBody)
}
})
}
}
func TestJsonParser_Unmarshal(t *testing.T) {
type args struct {
bodyType EntryMark
BodyEntry map[string]any
}
tests := []struct {
name string
args args
want io.Reader
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
js := JsonParser{}
if got := js.Unmarshal(tt.args.bodyType, tt.args.BodyEntry); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Unmarshal() = %v, want %v", got, tt.want)
}
})
}
}
func TestXmlParser_Unmarshal(t *testing.T) {
type args struct {
bodyType EntryMark
BodyEntry map[string]any
}
tests := []struct {
name string
args args
wantBody io.Reader
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := XmlParser{}
if gotBody := f.Unmarshal(tt.args.bodyType, tt.args.BodyEntry); !reflect.DeepEqual(gotBody, tt.wantBody) {
t.Errorf("Unmarshal() = %v, want %v", gotBody, tt.wantBody)
}
})
}
}
func Test_multipartCommonParse(t *testing.T) {
type args struct {
BodyEntry map[string]any
}
tests := []struct {
name string
args args
wantReader io.Reader
wantContentType string
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotReader, gotContentType := multipartCommonParse(tt.args.BodyEntry)
if !reflect.DeepEqual(gotReader, tt.wantReader) {
t.Errorf("multipartCommonParse() gotReader = %v, want %v", gotReader, tt.wantReader)
}
if gotContentType != tt.wantContentType {
t.Errorf("multipartCommonParse() gotContentType = %v, want %v", gotContentType, tt.wantContentType)
}
})
}
}

View File

@ -1,18 +1,17 @@
/* /*
*FileName: simpleRequest.go * @FileName: simpleRequest.go
*Author: JJXu * @Author: JJXu
*CreateTime: 2022/3/2 上午12:33 * @CreateTime: 2022/3/2 上午12:33
*Description: * @Description:
*/ */
package simpleRequest package simpleRequest
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -43,23 +42,20 @@ func NewRequest(opts ...OPTION) *SimpleRequest {
} }
type SimpleRequest struct { type SimpleRequest struct {
url string url string
queryParams url.Values queryParams url.Values
body io.Reader body io.Reader
headers http.Header headers http.Header
omitHeaderKeys []string transport *http.Transport
transport *http.Transport
BodyEntryMark EntryMark // 用于标记输入的body内容格式 BodyEntryMark EntryMark
BodyEntries map[string]any // 输入的body中的内容 BodyEntries map[string]any
bodyEntryParsers map[string]IBodyEntryParser // 用于将BodyEntries中的内容解析后传入request body中 bodyEntryParsers map[string]IBodyEntryParser
disableDefaultContentType bool // 禁用默认的ContentType
disableCopyRequestBody bool // 禁用复制请求体在进行http请求后SimpleRequest.Request.Body中内容会无法读取但是会提高性能
timeout time.Duration timeout time.Duration
Response *http.Response //用于获取完整的返回内容。请注意要在请求之后才能获取 Response http.Response //用于获取完整的返回内容。请注意要在请求之后才能获取
Request *http.Request //用于获取完整的请求内容。请注意要在请求之后才能获取 Request http.Request //用于获取完整的请求内容。请注意要在请求之后才能获取
//cookies map[string]string //cookies map[string]string
//data any //data any
//cli *http.Client //cli *http.Client
@ -88,35 +84,35 @@ type SimpleRequest struct {
// //
// 数据准备 // 数据准备
// Authorization 添加令牌的方法集合 //Authorization 添加令牌的方法集合
func (s *SimpleRequest) Authorization() *Authorization { func (s *SimpleRequest) Authorization() *Authorization {
return &Authorization{ return &Authorization{
simpleReq: s, simpleReq: s,
} }
} }
// Headers 添加请求头 //Headers 添加请求头
func (s *SimpleRequest) Headers() *HeadersConf { func (s *SimpleRequest) Headers() *HeadersConf {
return &HeadersConf{ return &HeadersConf{
simpleReq: s, simpleReq: s,
} }
} }
// Body 添加请求体 //Body 添加请求体
func (s *SimpleRequest) Body() *BodyConf { func (s *SimpleRequest) Body() *BodyConf {
return &BodyConf{ return &BodyConf{
simpleReq: s, simpleReq: s,
} }
} }
// QueryParams 添加url后面的参数 //QueryParams 添加url后面的参数
func (s *SimpleRequest) QueryParams() *QueryParams { func (s *SimpleRequest) QueryParams() *QueryParams {
return &QueryParams{ return &QueryParams{
simpleReq: s, simpleReq: s,
} }
} }
// 跳过证书验证 //跳过证书验证
func (s *SimpleRequest) SkipCertVerify() *SimpleRequest { func (s *SimpleRequest) SkipCertVerify() *SimpleRequest {
s.transport = &http.Transport{ s.transport = &http.Transport{
@ -125,17 +121,17 @@ func (s *SimpleRequest) SkipCertVerify() *SimpleRequest {
return s return s
} }
// 设置超时时间 //设置超时时间
func (s *SimpleRequest) TimeOut(t time.Duration) *SimpleRequest { func (s *SimpleRequest) TimeOut(t time.Duration) *SimpleRequest {
s.timeout = t s.timeout = t
return s return s
} }
// ------------------------------------------------------ //------------------------------------------------------
// //
// 发送请求 // 发送请求
// //
// 发送postt请求 //发送postt请求
func (s *SimpleRequest) do(request *http.Request) (body []byte, err error) { func (s *SimpleRequest) do(request *http.Request) (body []byte, err error) {
//3. 建立http客户端 //3. 建立http客户端
client := &http.Client{ client := &http.Client{
@ -147,76 +143,79 @@ func (s *SimpleRequest) do(request *http.Request) (body []byte, err error) {
//3.1 发送数据 //3.1 发送数据
resp, err := client.Do(request) resp, err := client.Do(request)
if err != nil { if err != nil {
err = errors.New("[client.Do Err]:" + err.Error()) fmt.Println("【Request Error】:", err.Error())
return return
} }
//v0.0.2更新将request和response内容返回便于用户进行分析 JJXu 03-11-2022 //v0.0.2更新将request和response内容返回便于用户进行分析 JJXu 03-11-2022
s.Request = request
if resp != nil { if resp != nil {
s.Response = resp s.Response = *resp
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
} }
if request != nil {
s.Request = *request
}
defer resp.Body.Close()
//3.2 获取数据
body, err = ioutil.ReadAll(resp.Body)
return return
} }
// POST method does POST HTTP request. It's defined in section 2 of RFC5789. // POST method does POST HTTP request. It's defined in section 2 of RFC5789.
func (s *SimpleRequest) POST(urls string) (body []byte, err error) { func (s *SimpleRequest) POST(urls string) (body []byte, err error) {
return s.LaunchTo(urls, http.MethodPost) s.initBody()
r, err := http.NewRequest(http.MethodPost, urls, s.body)
if err != nil {
return nil, err
}
//headers
for k := range s.headers {
r.Header[k] = append(r.Header[k], s.headers[k]...)
s.headers.Del(k)
}
//queryParams
r.URL.RawQuery = s.queryParams.Encode()
body, err = s.do(r)
return
} }
// GET method does GET HTTP request. It's defined in section 2 of RFC5789. // GET method does GET HTTP request. It's defined in section 2 of RFC5789.
func (s *SimpleRequest) GET(urls string) (body []byte, err error) { func (s *SimpleRequest) GET(urls string) (body []byte, err error) {
return s.LaunchTo(urls, http.MethodGet)
}
// 通用的请求方法
func (s *SimpleRequest) LaunchTo(urls, method string) (body []byte, err error) {
var r *http.Request
// body // body
s.initBody() s.initBody()
var copBody []byte r, err := http.NewRequest(http.MethodGet, urls, s.body)
if s.body != nil { if err != nil {
copBody, err = io.ReadAll(s.body) return nil, err
if err != nil {
return
}
}
if !s.disableCopyRequestBody {
// 使r.body在请求后仍旧可读,便于使用者对请求过程进行分析
r, err = http.NewRequest(method, urls, io.NopCloser(bytes.NewBuffer(copBody)))
if err != nil {
return nil, err
}
defer func() {
r.Body = io.NopCloser(bytes.NewBuffer(copBody))
}()
} else {
r, err = http.NewRequest(method, urls, s.body)
if err != nil {
return nil, err
}
} }
//headers //headers
r.Header = s.headers for k := range s.headers {
for _, k := range s.omitHeaderKeys { r.Header[k] = append(r.Header[k], s.headers[k]...)
r.Header.Del(k) s.headers.Del(k)
} }
//queryParams //queryParams
if r.URL.RawQuery != "" { r.URL.RawQuery = s.queryParams.Encode()
values, err := url.ParseQuery(r.URL.RawQuery)
if err == nil { body, err = s.do(r)
newValues := url.Values{} return
for k := range values { }
newValues.Set(k, values.Get(k))
} //通用的请求方法
for k := range s.queryParams { func (s *SimpleRequest) LaunchTo(urls, method string) (body []byte, err error) {
newValues.Set(k, s.queryParams.Get(k)) // body
} s.initBody()
s.queryParams = newValues r, err := http.NewRequest(method, urls, s.body)
} if err != nil {
return nil, err
} }
//headers
for k := range s.headers {
r.Header[k] = append(r.Header[k], s.headers[k]...)
s.headers.Del(k)
}
//queryParams
r.URL.RawQuery = s.queryParams.Encode() r.URL.RawQuery = s.queryParams.Encode()
body, err = s.do(r) body, err = s.do(r)
@ -258,103 +257,67 @@ func (s *SimpleRequest) TRACE(url string) (body []byte, err error) {
return s.LaunchTo(url, http.MethodTrace) return s.LaunchTo(url, http.MethodTrace)
} }
// ------------------------------------------------------ //------------------------------------------------------
//
// 这里数据
// //
// Automatically parses the request body based on the content-type type defined in the request header
func (s *SimpleRequest) initBody() { func (s *SimpleRequest) initBody() {
contentTypeData := s.headers.Get(hdrContentTypeKey) contentTypeData := s.headers.Get(hdrContentTypeKey)
if contentTypeData != "" { switch {
switch { case IsJSONType(contentTypeData):
case IsJSONType(contentTypeData): var parser, ok = s.bodyEntryParsers[jsonContentType]
var parser, ok = s.bodyEntryParsers[jsonContentType] if !ok {
if !ok { parser = new(JsonParser)
parser = new(JsonParser)
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
case strings.Contains(contentTypeData, formDataType):
var parser, ok = s.bodyEntryParsers[formDataType]
if !ok {
parser = new(FormDataParser)
}
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
var parser, ok = s.bodyEntryParsers[xmlDataType]
if !ok {
s.body = GetStringEntryTypeBody(s.BodyEntries)
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
case strings.Contains(contentTypeData, "text") || strings.Contains(contentTypeData, javaScriptType):
var parser, ok = s.bodyEntryParsers[textPlainType]
if !ok {
s.body = GetStringEntryTypeBody(s.BodyEntries)
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
case strings.Contains(contentTypeData, "form-urlencoded"):
//default header type is "x-www-form-urlencoded"
var parser, ok = s.bodyEntryParsers["form-urlencoded"]
if !ok {
for k, v := range s.BodyEntries {
if v == nil {
break
}
switch k {
case StringEntryType.string():
s.body = GetStringEntryTypeBody(s.BodyEntries)
break
case BytesEntryType.string():
s.body = GetBytesEntryTypeBody(s.BodyEntries)
break
default:
tmpData := url.Values{}
if strings.HasPrefix(k, FormFilePathKey.string()) {
k = k[len(FormFilePathKey):]
}
tmpData.Set(k, fmt.Sprintf("%v", v))
s.body = strings.NewReader(tmpData.Encode())
}
}
//s.Headers().ConentType_formUrlencoded()
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
default:
// 自动处理body数据
s.body = new(CommonParser).Unmarshal(s.BodyEntryMark, s.BodyEntries)
}
} else {
switch s.BodyEntryMark {
case BytesEntryType:
s.body = GetBytesEntryTypeBody(s.BodyEntries)
case StringEntryType:
s.body = GetStringEntryTypeBody(s.BodyEntries)
default:
var parser, ok = s.bodyEntryParsers["form-urlencoded"]
if !ok {
tmpData := url.Values{}
for k, v := range s.BodyEntries {
if strings.HasPrefix(k, FormFilePathKey.string()) {
k = k[len(FormFilePathKey):]
}
tmpData.Set(k, fmt.Sprintf("%v", v))
}
s.body = strings.NewReader(tmpData.Encode())
if !s.disableDefaultContentType {
s.Headers().ConentType_formUrlencoded()
}
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
} }
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
case strings.Contains(contentTypeData, formDataType):
var parser, ok = s.bodyEntryParsers[formDataType]
if !ok {
parser = new(FormDataParser)
}
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
var parser, ok = s.bodyEntryParsers[xmlDataType]
if !ok {
data, _ := s.BodyEntries[StringEntryType.string()].(string)
s.body = strings.NewReader(data)
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
case strings.Contains(contentTypeData, "text") || strings.Contains(contentTypeData, javaScriptType):
var parser, ok = s.bodyEntryParsers[textPlainType]
if !ok {
data, _ := s.BodyEntries[StringEntryType.string()].(string)
s.body = strings.NewReader(data)
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
case contentTypeData == "" || strings.Contains(contentTypeData, "form-urlencoded"):
//默认为x-www-form-urlencoded格式
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()
return
}
s.body = parser.Unmarshal(s.BodyEntryMark, s.BodyEntries)
default:
//todo 自动判断数据类型
tmpData := url.Values{}
for k, v := range tmpData {
tmpData.Set(k, fmt.Sprintf("%v", v))
}
s.body = strings.NewReader(tmpData.Encode())
} }
} }

View File

@ -1,13 +1,14 @@
// Package simpleRequest ----------------------------- // Package simpleRequest -----------------------------
// file : simpleRequest_test.go // @file : simpleRequest_test.go
// author : JJXu // @author : JJXu
// contact : wavingBear@163.com // @contact : wavingBear@163.com
// time : 2022/12/9 20:34:52 // @time : 2022/12/9 20:34:52
// ------------------------------------------- // -------------------------------------------
package simpleRequest package simpleRequest
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net/http" "net/http"
"testing" "testing"
@ -34,7 +35,7 @@ func httpserver() {
io.WriteString(w, "false") io.WriteString(w, "false")
} }
}) })
//fmt.Println("http服务启动了") fmt.Println("http服务启动了")
http.ListenAndServe(":8989", nil) http.ListenAndServe(":8989", nil)
} }
@ -83,78 +84,3 @@ func TestPost_withSetModel(t *testing.T) {
t.Log(string(result)) t.Log(string(result))
} }
} }
// url中的query param字符串参数会被r.QueryParams()中的key值覆盖
func TestQueryUrl2(t *testing.T) {
go httpserver()
var r = NewRequest()
r.Headers().ConentType_formUrlencoded()
r.QueryParams().Set("a", "123")
r.QueryParams().Set("b", "456")
_, err := r.POST("http://localhost:8989?a=1&b=2&c=3")
if err != nil {
t.Error(err.Error())
} else {
if r.Request.URL.RawQuery != "a=123&b=456&c=3" {
t.Errorf("query params want '%s' but get '%s'", "a=123&b=456&c=3", r.Request.URL.RawQuery)
}
}
}
// 请求后r.Request.Body中的内容仍旧可读
func TestQueryUseStringBody(t *testing.T) {
go httpserver()
var r = NewRequest()
r.Headers().ConentType_json()
bodyData := "{'a'=123,'b'=56}"
r.Body().SetString(bodyData)
_, err := r.POST("http://localhost:8989")
if err != nil {
t.Error(err)
return
}
body, err := io.ReadAll(r.Request.Body)
if err != nil {
t.Error(err)
return
}
if string(body) != bodyData {
t.Errorf("request body want '%s' but get '%s'", bodyData, body)
}
}
func TestEmptyBody(t *testing.T) {
go httpserver()
var r = NewRequest()
r.Headers().ConentType_json()
_, err := r.POST("http://localhost:8989")
if err != nil {
t.Error(err)
return
}
body, err := io.ReadAll(r.Request.Body)
if err != nil {
t.Error(err)
return
}
if string(body) != "{}" {
t.Errorf("request body want '%s' but get '%s'", "{}", body)
}
r.Headers().ConentType_formUrlencoded()
_, err = r.POST("http://localhost:8989")
if err != nil {
t.Error(err)
return
}
body, err = io.ReadAll(r.Request.Body)
if err != nil {
t.Error(err)
return
}
if string(body) != "" {
t.Errorf("request body want '%s' but get '%s'", "", body)
}
}

103
utils.go
View File

@ -1,17 +1,12 @@
/* /*
*FileName: utils.go * @FileName: utils.go
*Author: JJXu * @Author: JJXu
*CreateTime: 2022/3/29 上午11:16 * @CreateTime: 2022/3/29 上午11:16
*Description: * @Description:
*/ */
package simpleRequest package simpleRequest
import (
"encoding/xml"
"fmt"
)
func IsJSONType(ct string) bool { func IsJSONType(ct string) bool {
return jsonCheck.MatchString(ct) return jsonCheck.MatchString(ct)
} }
@ -20,93 +15,3 @@ func IsJSONType(ct string) bool {
func IsXMLType(ct string) bool { func IsXMLType(ct string) bool {
return xmlCheck.MatchString(ct) return xmlCheck.MatchString(ct)
} }
func IsInArray(arr []string, str string) bool {
for _, v := range arr {
if v == str {
return true
}
}
return false
}
type xmlMapEntry struct {
XMLName xml.Name
Value interface{} `xml:",chardata"`
}
// func MapToXml(data map[string]any) ([]byte, error) {
// xmlData, err := mapToXML(data)
// if err != nil {
// return nil, err
// }
// return xml.MarshalIndent(xmlData, "", " ")
// }
//
// func mapToXML(m map[string]interface{}) (xmlMap map[string]xmlMapEntry, err error) {
// if len(m) > 1 {
// return nil, errors.New("xml format must have a root name,the map value must like this: map[string]interface{}{\"rootName\":map[string]interface{}{}}")
// }
// xmlMap = make(map[string]xmlMapEntry)
// var rootName string
// for root, data := range m {
// rootName = root
// for k, v := range data.(map[string]interface{}) {
// switch typeV := v.(type) {
// case map[string]interface{}:
// subXmlMap, err := mapToXML(typeV)
// if err != nil {
// return
// }
//
// default:
// entry := xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v}
// xmlMap[k] = entry
// }
// }
// }
//
// xmlData := struct {
// XMLName xml.Name
// Data []xmlMapEntry `xml:",any"`
// }{
// XMLName: xml.Name{Local: rootName},
// Data: make([]xmlMapEntry, 0, len(xmlMap)),
// }
//
// for _, v := range xmlMap {
// xmlData.Data = append(xmlData.Data, v)
// }
//
// return xml.MarshalIndent(xmlData, "", " ")
// }
func mapToXML(m map[string]interface{}) ([]byte, error) {
xmlData := make([]xmlNode, 0)
for k, v := range m {
node := xmlNode{
XMLName: xml.Name{Local: k},
}
switch value := v.(type) {
case map[string]interface{}:
childXML, err := mapToXML(value)
if err != nil {
return nil, err
}
node.Data = childXML
default:
node.Data = []byte(fmt.Sprintf("%v", v))
}
xmlData = append(xmlData, node)
}
return xml.MarshalIndent(xmlData, "", " ")
}
type xmlNode struct {
XMLName xml.Name
Data []byte `xml:",innerxml"`
}

View File

@ -1,82 +0,0 @@
// Package simpleRequest -----------------------------
// @file : utils_test.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2023/11/17 16:37
// -------------------------------------------
package simpleRequest
import (
"fmt"
"testing"
)
func Test_mapToXML(t *testing.T) {
type args struct {
m map[string]interface{}
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "多级xml测试",
args: args{
m: map[string]interface{}{
"UserInfo": map[string]any{
"Name": "JJXu",
"Age": 18,
"isTrueMan": true,
"assets": map[string]any{
"car": "BMW",
"house": "shanghai",
},
},
},
},
want: "<UserInfo>\n<Age>18</Age>\n<isTrueMan>true</isTrueMan>\n<assets></assets>\n<Name>JJXu</Name>\n</UserInfo>",
wantErr: false,
},
{
name: "错误格式测试",
args: args{},
want: "",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := mapToXML(tt.args.m)
if (err != nil) != tt.wantErr {
t.Errorf("mapToXML() error = %v, wantErr %v", err, tt.wantErr)
return
}
if string(got) != tt.want {
t.Errorf("mapToXML() got = %v, want %v", string(got), tt.want)
}
})
}
}
func Test_mapToXML2(t *testing.T) {
person := map[string]interface{}{
"userInfo": map[string]interface{}{
"name": "John",
"age": 30,
"address": map[string]interface{}{
"street": "123 Main St",
"city": "New York",
},
},
}
xmlBytes, err := mapToXML(person)
if err != nil {
fmt.Println("Error:", err)
return
}
xmlString := string(xmlBytes)
fmt.Println(xmlString)
}