Compare commits
No commits in common. "master" and "v1.0.4" have entirely different histories.
231
README.MD
231
README.MD
@ -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,202 +82,93 @@ 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]interface{})
|
||||||
pamarsBulid["passwd"] = "123456"
|
pamarsBulid["passwd"] = "123456"
|
||||||
pamarsBulid["action"] = "login"
|
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赋值同时使用
|
|
||||||
```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]interface{}{
|
||||||
"beginDate":"2022-03-01",
|
"beginDate":"2022-03-01",
|
||||||
"endDate":"2022-03-03",
|
"endDate":"2022-03-03",
|
||||||
}
|
}
|
||||||
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 字符串赋值
|
||||||
此方法为一次性赋值,不支持和其它赋值方法同时使用
|
json格式不要使用此方法
|
||||||
```go
|
```go
|
||||||
bodydata:=`{"devSn":"230000000008","type":"day"}`
|
bodydata:=`{"devSn":"230000000008","type":"day"}`
|
||||||
r.Body().SetString(bodydata)
|
r.Body().SetString(bodydata)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2.6.5 字节赋值
|
### 2.6 其它请求参数
|
||||||
此方法为一次性赋值,不支持和其它赋值方法同时使用
|
|
||||||
```go
|
|
||||||
bytesdata:=[]byte(`{"devSn":"230000000008","type":"day"}`)
|
|
||||||
r.Body().SetBytes(bytesdata)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.6.6 结构体赋值
|
#### 2.6.1 设置超时时间
|
||||||
此方法为一次性赋值,不支持和其它赋值方法同时使用
|
|
||||||
```go
|
|
||||||
type demo struct{
|
|
||||||
DevSn string `json:"devSn"`
|
|
||||||
Type string `json:"day"`
|
|
||||||
}
|
|
||||||
modeldata:=demo{
|
|
||||||
DevSn:"230000000008"
|
|
||||||
Type:"day"
|
|
||||||
}
|
|
||||||
r.Body().SetModel(&modeldata)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.7 文件上传与转发
|
|
||||||
### 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()
|
|
||||||
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 +181,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
|
||||||
```
|
```
|
||||||
|
@ -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))
|
||||||
|
90
body.go
90
body.go
@ -1,99 +1,31 @@
|
|||||||
/*
|
/*
|
||||||
*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 (
|
var (
|
||||||
"bytes"
|
stringBodyType = "__STRING_BODY__"
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EntryMark 请求体条目标记,用于标记输入的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__"
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BodyConf) Set(key string, value any) *BodyConf {
|
func (s *BodyConf) Set(key string, value interface{}) *BodyConf {
|
||||||
s.simpleReq.BodyEntries[key] = value
|
s.simpleReq.tempBody[key] = value
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (s *BodyConf) Sets(data map[string]any) *BodyConf {
|
func (s *BodyConf) Sets(data map[string]interface{}) *BodyConf {
|
||||||
s.simpleReq.BodyEntryMark = MapEntryType
|
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
s.simpleReq.BodyEntries[k] = v
|
s.simpleReq.tempBody[k] = v
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
func (s *BodyConf) SetString(strData string) *BodyConf {
|
func (s *BodyConf) SetString(strData string) *BodyConf {
|
||||||
s.simpleReq.BodyEntryMark = StringEntryType
|
s.simpleReq.tempBody[stringBodyType] = strData
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加上传文件
|
|
||||||
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
|
return s
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
@ -27,7 +26,7 @@ func TestRequest(t *testing.T) {
|
|||||||
//设置params
|
//设置params
|
||||||
r.QueryParams().Set("user", "dorlolo")
|
r.QueryParams().Set("user", "dorlolo")
|
||||||
//批量添加,不会覆盖上面user
|
//批量添加,不会覆盖上面user
|
||||||
pamarsBulid := map[string]any{
|
pamarsBulid := map[string]interface{}{
|
||||||
"passwd": "123456",
|
"passwd": "123456",
|
||||||
"action": "login",
|
"action": "login",
|
||||||
}
|
}
|
||||||
@ -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))
|
|
||||||
}
|
|
||||||
|
2
go.mod
2
go.mod
@ -1,3 +1,3 @@
|
|||||||
module github.com/dorlolo/simpleRequest
|
module github.com/dorlolo/simpleRequest
|
||||||
|
|
||||||
go 1.18
|
go 1.17
|
||||||
|
45
headers.go
45
headers.go
@ -1,7 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* @FileName: header.go
|
||||||
|
* @Author: JJXu
|
||||||
|
* @CreateTime: 2022/3/1 下午9:44
|
||||||
|
* @Description:
|
||||||
|
*/
|
||||||
|
|
||||||
package simpleRequest
|
package simpleRequest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -28,7 +36,7 @@ var (
|
|||||||
|
|
||||||
jsonCheck = regexp.MustCompile(`(?i:(application|text)/(json|.*\+json|json\-.*)(;|$))`)
|
jsonCheck = regexp.MustCompile(`(?i:(application|text)/(json|.*\+json|json\-.*)(;|$))`)
|
||||||
xmlCheck = regexp.MustCompile(`(?i:(application|text)/(xml|.*\+xml)(;|$))`)
|
xmlCheck = regexp.MustCompile(`(?i:(application|text)/(xml|.*\+xml)(;|$))`)
|
||||||
bufPool = &sync.Pool{New: func() any { return &bytes.Buffer{} }}
|
bufPool = &sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}
|
||||||
)
|
)
|
||||||
|
|
||||||
var userAgentList = [...]string{
|
var userAgentList = [...]string{
|
||||||
@ -44,7 +52,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 +60,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 +71,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,19 +88,24 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *HeadersConf) ConentType_json() *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)
|
s.simpleReq.headers.Set(hdrContentTypeKey, jsonContentType)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -109,6 +116,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.tempBody {
|
||||||
|
// 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 +133,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 +147,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
|
||||||
|
34
options.go
34
options.go
@ -1,34 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
18
param.go
18
param.go
@ -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,21 +16,21 @@ 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]interface{}) *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))
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// single settings
|
//single settings
|
||||||
func (s *QueryParams) Set(key string, value any) *QueryParams {
|
func (s *QueryParams) Set(key string, value interface{}) *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
|
||||||
}
|
}
|
||||||
|
195
parser.go
195
parser.go
@ -1,195 +0,0 @@
|
|||||||
// Package sRequest -----------------------------
|
|
||||||
// file : parser.go
|
|
||||||
// author : JJXu
|
|
||||||
// contact : wavingBear@163.com
|
|
||||||
// time : 2022/12/10 00:48:45
|
|
||||||
// -------------------------------------------
|
|
||||||
package simpleRequest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 通用类型解析器
|
|
||||||
var bodyEntryParsers = map[string]IBodyEntryParser{
|
|
||||||
jsonContentType: new(JsonParser),
|
|
||||||
formDataType: new(FormDataParser),
|
|
||||||
xmlDataType: new(XmlParser),
|
|
||||||
}
|
|
||||||
|
|
||||||
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 GetStringEntryTypeBody(BodyEntry)
|
|
||||||
case BytesEntryType:
|
|
||||||
return GetBytesEntryTypeBody(BodyEntry)
|
|
||||||
case ModelEntryType:
|
|
||||||
jsonData, err := json.Marshal(BodyEntry[ModelEntryType.string()])
|
|
||||||
if err == nil {
|
|
||||||
return bytes.NewReader(jsonData)
|
|
||||||
}
|
|
||||||
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:
|
|
||||||
return GetStringEntryTypeBody(BodyEntry)
|
|
||||||
case BytesEntryType:
|
|
||||||
return GetBytesEntryTypeBody(BodyEntry)
|
|
||||||
default:
|
|
||||||
body, f.ContentType = multipartCommonParse(BodyEntry)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func multipartCommonParse(BodyEntry map[string]any) (reader io.Reader, contentType string) {
|
|
||||||
body := &bytes.Buffer{}
|
|
||||||
formWriter := multipart.NewWriter(body)
|
|
||||||
for k, sv := range BodyEntry {
|
|
||||||
if strings.Contains(k, FormFilePathKey.string()) {
|
|
||||||
fieldName := k[len(FormFilePathKey):]
|
|
||||||
fp := sv.(string)
|
|
||||||
filename := filepath.Base(fp)
|
|
||||||
//way1
|
|
||||||
filePart, _ := formWriter.CreateFormFile(fieldName, filename)
|
|
||||||
content, err := os.ReadFile(fp)
|
|
||||||
if err != nil {
|
|
||||||
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()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return body, formWriter.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
|
|
||||||
}
|
|
118
parser_test.go
118
parser_test.go
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
336
simpleRequest.go
336
simpleRequest.go
@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
*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
|
||||||
@ -10,58 +10,47 @@ package simpleRequest
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRequest(opts ...OPTION) *SimpleRequest {
|
func NewRequest() *SimpleRequest {
|
||||||
var (
|
var (
|
||||||
hd = http.Header{}
|
hd = http.Header{}
|
||||||
qp = url.Values{}
|
qp = url.Values{}
|
||||||
)
|
)
|
||||||
|
|
||||||
var r = &SimpleRequest{
|
return &SimpleRequest{
|
||||||
//headers: make(map[string]string),
|
//headers: make(map[string]string),
|
||||||
//cookies: make(map[string]string),
|
//cookies: make(map[string]string),
|
||||||
timeout: time.Second * 7,
|
timeout: time.Second * 7,
|
||||||
queryParams: qp,
|
queryParams: qp,
|
||||||
headers: hd,
|
headers: hd,
|
||||||
BodyEntries: make(map[string]any),
|
tempBody: make(map[string]interface{}),
|
||||||
bodyEntryParsers: bodyEntryParsers,
|
|
||||||
}
|
}
|
||||||
if len(opts) > 0 {
|
|
||||||
for _, o := range opts {
|
|
||||||
r = o(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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内容格式
|
tempBody map[string]interface{}
|
||||||
BodyEntries map[string]any // 输入的body中的内容
|
timeout time.Duration
|
||||||
bodyEntryParsers map[string]IBodyEntryParser // 用于将BodyEntries中的内容解析后传入request body中
|
|
||||||
disableDefaultContentType bool // 禁用默认的ContentType
|
|
||||||
disableCopyRequestBody bool // 禁用复制请求体,在进行http请求后SimpleRequest.Request.Body中内容会无法读取,但是会提高性能
|
|
||||||
|
|
||||||
timeout time.Duration
|
Response http.Response //用于获取完整的返回内容。请注意要在请求之后才能获取
|
||||||
|
Request http.Request //用于获取完整的请求内容。请注意要在请求之后才能获取
|
||||||
Response *http.Response //用于获取完整的返回内容。请注意要在请求之后才能获取
|
|
||||||
Request *http.Request //用于获取完整的请求内容。请注意要在请求之后才能获取
|
|
||||||
//cookies map[string]string
|
//cookies map[string]string
|
||||||
//data any
|
//data interface{}
|
||||||
//cli *http.Client
|
//cli *http.Client
|
||||||
//debug bool
|
//debug bool
|
||||||
//method string
|
//method string
|
||||||
@ -73,50 +62,50 @@ type SimpleRequest struct {
|
|||||||
//checkRedirect func(req *http.Request, via []*http.Request) error
|
//checkRedirect func(req *http.Request, via []*http.Request) error
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (s *SimpleRequest) NewRequest() *SimpleRequest {
|
func (s *SimpleRequest) NewRequest() *SimpleRequest {
|
||||||
// var qp = url.Values{}
|
var qp = url.Values{}
|
||||||
// return &SimpleRequest{
|
return &SimpleRequest{
|
||||||
// //headers: make(map[string]string),
|
//headers: make(map[string]string),
|
||||||
// //cookies: make(map[string]string),
|
//cookies: make(map[string]string),
|
||||||
// timeout: time.Second * 7,
|
timeout: time.Second * 7,
|
||||||
// queryParams: qp,
|
queryParams: qp,
|
||||||
// BodyEntries: make(map[string]any),
|
tempBody: make(map[string]interface{}),
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
//
|
//
|
||||||
// 数据准备
|
// 数据准备
|
||||||
|
|
||||||
// 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 +114,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 +136,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 +250,63 @@ 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):
|
jsonData, err := json.Marshal(s.tempBody)
|
||||||
var parser, ok = s.bodyEntryParsers[jsonContentType]
|
if err == nil {
|
||||||
if !ok {
|
s.body = bytes.NewReader(jsonData)
|
||||||
parser = new(JsonParser)
|
} else {
|
||||||
}
|
s.body = bytes.NewReader([]byte("{}"))
|
||||||
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 {
|
case strings.Contains(contentTypeData, "multipart/form-data"):
|
||||||
switch s.BodyEntryMark {
|
body := &bytes.Buffer{}
|
||||||
case BytesEntryType:
|
writer := multipart.NewWriter(body)
|
||||||
s.body = GetBytesEntryTypeBody(s.BodyEntries)
|
//data := url.Values{}
|
||||||
case StringEntryType:
|
for k, sv := range s.tempBody {
|
||||||
s.body = GetStringEntryTypeBody(s.BodyEntries)
|
switch sv.(type) {
|
||||||
default:
|
case string:
|
||||||
var parser, ok = s.bodyEntryParsers["form-urlencoded"]
|
strSv, _ := sv.(string)
|
||||||
if !ok {
|
_ = writer.WriteField(k, strSv)
|
||||||
tmpData := url.Values{}
|
case []string:
|
||||||
for k, v := range s.BodyEntries {
|
sss, _ := sv.([]string)
|
||||||
if strings.HasPrefix(k, FormFilePathKey.string()) {
|
for _, v := range sss {
|
||||||
k = k[len(FormFilePathKey):]
|
_ = writer.WriteField(k, v)
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
err := writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.headers.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
s.body = body
|
||||||
|
case IsXMLType(contentTypeData):
|
||||||
|
//application/soap+xml ,application/xml
|
||||||
|
data, _ := s.tempBody[stringBodyType].(string)
|
||||||
|
s.body = strings.NewReader(data)
|
||||||
|
case strings.Contains(contentTypeData, "text") || strings.Contains(contentTypeData, javaScriptType):
|
||||||
|
data, _ := s.tempBody[stringBodyType].(string)
|
||||||
|
s.body = strings.NewReader(data)
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
s.body = strings.NewReader(tmpData.Encode())
|
||||||
|
s.Headers().ConentType_formUrlencoded()
|
||||||
|
default:
|
||||||
|
//todo 自动判断数据类型
|
||||||
|
tmpData := url.Values{}
|
||||||
|
for k, v := range tmpData {
|
||||||
|
tmpData.Set(k, fmt.Sprintf("%v", v))
|
||||||
|
}
|
||||||
|
s.body = strings.NewReader(tmpData.Encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ func TestPost_withSets(t *testing.T) {
|
|||||||
|
|
||||||
var r = NewRequest()
|
var r = NewRequest()
|
||||||
r.Headers().ConentType_json()
|
r.Headers().ConentType_json()
|
||||||
r.Body().Sets(map[string]any{
|
r.Body().Sets(map[string]interface{}{
|
||||||
"name": "JJXu",
|
"name": "JJXu",
|
||||||
})
|
})
|
||||||
result, err := r.POST("http://localhost:8989/")
|
result, err := r.POST("http://localhost:8989/")
|
||||||
@ -66,95 +67,3 @@ func TestPost_withSets(t *testing.T) {
|
|||||||
t.Log(string(result))
|
t.Log(string(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPost_withSetModel(t *testing.T) {
|
|
||||||
go httpserver()
|
|
||||||
|
|
||||||
var r = NewRequest()
|
|
||||||
r.Headers().ConentType_json()
|
|
||||||
var entry = api{
|
|
||||||
Name: "JJXu",
|
|
||||||
}
|
|
||||||
r.Body().SetModel(&entry)
|
|
||||||
result, err := r.POST("http://localhost:8989/")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
} else {
|
|
||||||
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
103
utils.go
@ -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"`
|
|
||||||
}
|
|
||||||
|
@ -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)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user