改版前

This commit is contained in:
huangsimin 2018-10-17 14:25:17 +08:00
commit dc1baf12c1
14 changed files with 776 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pyc

42
base.go Normal file
View File

@ -0,0 +1,42 @@
package requests
import (
"bytes"
"errors"
"net/http"
"reflect"
)
func buildBodyRequest(ver, url string, params *Params) *http.Request {
var req *http.Request
var err error
if params.Body == nil {
req, err = http.NewRequest(ver, url, nil)
} else {
var body *bytes.Buffer
switch params.Body.(type) {
case []byte:
body = bytes.NewBuffer(params.Body.([]byte))
case *bytes.Buffer:
body = bytes.NewBuffer(params.Body.(*bytes.Buffer).Bytes())
default:
panic(errors.New("the type is not exist, type is" + reflect.TypeOf(params.Body).String()))
}
req, err = http.NewRequest(ver, url, body)
}
if err != nil {
panic(err)
}
if params.ContentType == "" {
req.Header.Set("Content-Type", TypeURLENCODED)
} else {
req.Header.Set("Content-Type", params.ContentType)
}
return req
}

5
httpclient_method.go Normal file
View File

@ -0,0 +1,5 @@
package requests
func Get(url string) {
}

View File

@ -0,0 +1,9 @@
package requests
import (
"testing"
)
func TestGet(t *testing.T) {
}

91
multipart.go Normal file
View File

@ -0,0 +1,91 @@
package requests
import (
"bytes"
"io"
"log"
"mime/multipart"
"net/url"
"strconv"
)
func writeFormUploadFile(mwriter *multipart.Writer, ufile *UploadFile) {
part, err := mwriter.CreateFormFile(ufile.FieldName, ufile.FileName)
if err != nil {
log.Panic(err)
}
io.Copy(part, ufile.FileReaderCloser)
}
func createMultipart(postParams *Params, params []interface{}) {
plen := len(params)
body := &bytes.Buffer{}
mwriter := multipart.NewWriter(body)
defer mwriter.Close()
for _, iparam := range params[0 : plen-1] {
switch param := iparam.(type) {
case *UploadFile:
if param.FieldName == "" {
param.FieldName = "file0"
}
writeFormUploadFile(mwriter, param)
case []*UploadFile:
for i, p := range param {
if p.FieldName == "" {
p.FieldName = "file" + strconv.Itoa(i)
}
writeFormUploadFile(mwriter, p)
}
case string:
log.Println(param)
uploadFiles, err := UploadFileFromGlob(param)
if err != nil {
log.Println(err)
} else {
for i, p := range uploadFiles {
if p.FieldName == "" {
p.FieldName = "file" + strconv.Itoa(i)
}
writeFormUploadFile(mwriter, p)
}
}
case []string:
for i, glob := range param {
uploadFiles, err := UploadFileFromGlob(glob)
if err != nil {
log.Println(err)
} else {
for ii, p := range uploadFiles {
if p.FieldName == "" {
p.FieldName = "file" + strconv.Itoa(ii) + "_" + strconv.Itoa(i)
}
writeFormUploadFile(mwriter, p)
}
}
}
case map[string]string:
for k, v := range param {
mwriter.WriteField(k, v)
}
case map[string][]string:
for k, vs := range param {
for _, v := range vs {
mwriter.WriteField(k, v)
}
}
case url.Values:
for k, vs := range param {
for _, v := range vs {
mwriter.WriteField(k, v)
}
}
}
}
postParams.ContentType = mwriter.FormDataContentType()
postParams.Body = body
}

149
requests_method.go Normal file
View File

@ -0,0 +1,149 @@
package requests
import (
"net/http"
"net/url"
)
// Params 相关参数结构
type Params struct {
// Query map[string][]string
Body interface{}
// Files []UploadFile
ContentType string
}
// Session 的基本方法
type Session struct {
client *http.Client
params *Params
}
// TypeContent post类型参数
type TypeContent int
const (
_ TypeContent = iota
// TypeJSON 类型
TypeJSON = "application/json"
// TypeXML 类型
TypeXML = "text/xml"
// TypeURLENCODED 类型
TypeURLENCODED = "application/x-www-form-urlencoded"
// TypeFormData 类型
TypeFormData = "multipart/form-data"
)
// NewSession 创建Session
func NewSession() *Session {
return &Session{client: &http.Client{}, params: &Params{}}
}
// Get 请求
func (ses *Session) Get(url string) (*Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}
// SetParams 设置Post参数
func (ses *Session) SetParams(params ...interface{}) {
plen := len(params)
defaultContentType := TypeURLENCODED
if plen >= 2 {
t := params[plen-1]
defaultContentType = t.(string)
ses.params.ContentType = defaultContentType
} else {
ses.params.ContentType = defaultContentType
}
if defaultContentType == TypeFormData {
// TODO: form-data
createMultipart(ses.params, params)
} else {
var values url.Values
switch param := params[0].(type) {
case map[string]string:
values := make(url.Values)
for k, v := range param {
values.Set(k, v)
}
ses.params.Body = []byte(values.Encode())
case map[string][]string:
values = param
ses.params.Body = []byte(values.Encode())
case string:
ses.params.Body = []byte(param)
case []byte:
ses.params.Body = param
}
}
}
// Post 请求
func (ses *Session) Post(url string) (*Response, error) {
req := buildBodyRequest("POST", url, ses.params)
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}
// Put 请求
func (ses *Session) Put(url string) (*Response, error) {
req := buildBodyRequest("PUT", url, ses.params)
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}
// Patch 请求
func (ses *Session) Patch(url string) (*Response, error) {
req := buildBodyRequest("PATCH", url, ses.params)
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}
// Delete 请求
func (ses *Session) Delete(url string) (*Response, error) {
req := buildBodyRequest("DELETE", url, ses.params)
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}
// Head 请求
func (ses *Session) Head(url string) (*Response, error) {
req := buildBodyRequest("HEAD", url, ses.params)
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}
// Options 请求
func (ses *Session) Options(url string) (*Response, error) {
req := buildBodyRequest("OPTIONS", url, ses.params)
resp, err := ses.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}

285
requests_method_test.go Normal file
View File

@ -0,0 +1,285 @@
package requests
import (
"net/http"
"regexp"
"testing"
)
func TestNewSession(t *testing.T) {
ses := NewSession()
if ses == nil {
t.Error("session create fail, value is nil")
}
}
func TestSession_Get(t *testing.T) {
type fields struct {
client *http.Client
}
type args struct {
url string
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
{
name: "Get test",
fields: fields{client: &http.Client{}},
args: args{url: "http://httpbin.org/get"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ses := &Session{
client: tt.fields.client,
}
resp, err := ses.Get(tt.args.url)
if err != nil {
t.Error(err)
}
if len(resp.Content()) <= 200 {
t.Error(resp.Content())
}
})
}
}
func TestSession_Post(t *testing.T) {
type fields struct {
client *http.Client
params *Params
}
type args struct {
url string
}
tests := []struct {
name string
fields fields
args args
want *regexp.Regexp
wantErr bool
}{
// TODO: Add test cases.
{
name: "Post test",
fields: fields{client: &http.Client{}, params: &Params{}},
args: args{url: "http://httpbin.org/post"},
want: regexp.MustCompile(`"form": \{\}`),
},
{
name: "Post data",
fields: fields{client: &http.Client{}, params: &Params{Body: []byte("a=1&b=2")}},
args: args{url: "http://httpbin.org/post"},
want: regexp.MustCompile(`"form": \{[^"]+"a": "1"[^"]+"b": "2"[^\}]+\}`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ses := &Session{
client: tt.fields.client,
params: tt.fields.params,
}
got, err := ses.Post(tt.args.url)
if (err != nil) != tt.wantErr {
t.Errorf("Session.Post() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.want.MatchString(got.DContent) == false {
t.Errorf("Session.Post() = %v, want %v", got, tt.want)
}
})
}
}
func TestSession_Setparams(t *testing.T) {
type fields struct {
client *http.Client
params *Params
}
type args struct {
params []interface{}
}
tests := []struct {
name string
fields fields
args args
want *regexp.Regexp
wantErr bool
}{
// TODO: Add test cases.
{
name: "test Setparams",
fields: fields{client: &http.Client{}, params: &Params{}},
args: args{params: []interface{}{map[string]string{"a": "1", "b": "2"}}},
want: regexp.MustCompile(`"form": \{[^"]+"a": "1"[^"]+"b": "2"[^\}]+\}`),
},
{
name: "test json",
fields: fields{client: &http.Client{}, params: &Params{}},
args: args{params: []interface{}{`{"a":"1","b":"2"}`, TypeJSON}},
want: regexp.MustCompile(`"json": \{[^"]+"a": "1"[^"]+"b": "2"[^\}]+\}`),
},
{
name: "test xml",
fields: fields{client: &http.Client{}, params: &Params{}},
args: args{params: []interface{}{`<request><parameters><password>test</password></parameters></request>`, TypeXML}},
want: regexp.MustCompile(`"data": "<request><parameters><password>test</password></parameters></request>"`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ses := NewSession()
ses.SetParams(tt.args.params...)
got, err := ses.Post("http://httpbin.org/post")
if (err != nil) != tt.wantErr {
t.Errorf("Session.Post() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.want.MatchString(got.DContent) == false {
t.Errorf("Session.Post() = %v, want %v", got, tt.want)
}
})
}
}
func TestSession_PostUploadFile(t *testing.T) {
type args struct {
params interface{}
}
tests := []struct {
name string
args args
want *regexp.Regexp
}{
{
name: "test post uploadfile glob",
args: args{params: "tests/*.js"},
want: regexp.MustCompile(`"file0": "data:application/octet-stream;base64`),
},
{
name: "test post uploadfile only one file",
args: args{params: "tests/json.file"},
want: regexp.MustCompile(`"file0": "json.file.+jsonjsonjsonjson"`),
},
{
name: "test post uploadfile key values",
args: args{params: map[string]string{"a": "32"}},
want: regexp.MustCompile(`"a": "32"`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ses := NewSession()
ses.SetParams(tt.args.params, TypeFormData)
got, err := ses.Post("http://httpbin.org/post")
if err != nil {
t.Errorf("Session.Post() error = %v", err)
return
}
if tt.want.MatchString(got.DContent) == false {
t.Errorf("Session.Post() = %v, want %v", got, tt.want)
}
})
}
}
func TestSession_Put(t *testing.T) {
type args struct {
params interface{}
}
tests := []struct {
name string
args args
want *regexp.Regexp
}{
{
name: "test post uploadfile glob",
args: args{params: "tests/*.js"},
want: regexp.MustCompile(`"file0": "data:application/octet-stream;base64`),
},
{
name: "test post uploadfile only one file",
args: args{params: "tests/json.file"},
want: regexp.MustCompile(`"file0": "json.file.+jsonjsonjsonjson"`),
},
{
name: "test post uploadfile key values",
args: args{params: map[string]string{"a": "32"}},
want: regexp.MustCompile(`"a": "32"`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ses := NewSession()
ses.SetParams(tt.args.params, TypeFormData)
got, err := ses.Put("http://httpbin.org/put")
if err != nil {
t.Errorf("Session.Post() error = %v", err)
return
}
if tt.want.MatchString(got.DContent) == false {
t.Errorf("Session.Post() = %v, want %v", got, tt.want)
}
})
}
}
func TestSession_Patch(t *testing.T) {
type args struct {
params interface{}
}
tests := []struct {
name string
args args
want *regexp.Regexp
}{
{
name: "test post uploadfile glob",
args: args{params: "tests/*.js"},
want: regexp.MustCompile(`"file0": "data:application/octet-stream;base64`),
},
{
name: "test post uploadfile only one file",
args: args{params: "tests/json.file"},
want: regexp.MustCompile(`"file0": "json.file.+jsonjsonjsonjson"`),
},
{
name: "test post uploadfile key values",
args: args{params: map[string]string{"a": "32"}},
want: regexp.MustCompile(`"a": "32"`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ses := NewSession()
ses.SetParams(tt.args.params, TypeFormData)
got, err := ses.Patch("http://httpbin.org/patch")
if err != nil {
t.Errorf("Session.Post() error = %v", err)
return
}
if tt.want.MatchString(got.DContent) == false {
t.Errorf("Session.Post() = %v, want %v", got, tt.want)
}
})
}
}

60
response.go Normal file
View File

@ -0,0 +1,60 @@
package requests
import (
"bytes"
"compress/gzip"
"compress/zlib"
"io"
"io/ioutil"
"net/http"
)
// Response 响应内容包含http.Response
type Response struct {
DContent string
GResponse *http.Response
}
// FromHTTPResponse 生成Response 从标准http.Response
func FromHTTPResponse(resp *http.Response) (*Response, error) {
// 复制response 返回内容 并且测试是否有解压的需求
srcbuf, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
resp.Body.Close()
cbuf := bytes.NewBuffer([]byte{})
_, err = io.Copy(cbuf, bytes.NewReader(srcbuf))
if err != nil {
panic(err)
}
resp.Body = ioutil.NopCloser(cbuf)
content := string(srcbuf)
srcReader := bytes.NewReader(srcbuf)
if r, err := gzip.NewReader(srcReader); err == nil {
buf, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
content = string(buf)
} else if r, err := zlib.NewReader(srcReader); err == nil {
buf, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
content = string(buf)
}
return &Response{DContent: content, GResponse: resp}, nil
}
// Content 返回解压后的内容
func (gresp *Response) Content() string {
return gresp.DContent
}

1
response_test.go Normal file
View File

@ -0,0 +1 @@
package requests

BIN
tests/file.js Normal file

Binary file not shown.

2
tests/json.file Normal file
View File

@ -0,0 +1,2 @@
json.file
fdsfsdavxvxwewe32323412jsonjsonjsonjson

2
tests/learn.js Normal file
View File

@ -0,0 +1,2 @@
learn.js
fdsfsdavxlearnlearnlearnlearn

58
upload_file.go Normal file
View File

@ -0,0 +1,58 @@
package requests
import (
"io"
"log"
"os"
"path/filepath"
)
// UploadFile 上传文件的结构
type UploadFile struct {
FileName string
FieldName string
FileReaderCloser io.ReadCloser
}
// UploadFileFromPath 从本地文件获取上传文件
func UploadFileFromPath(fileName string) (*UploadFile, error) {
fd, err := os.Open(fileName)
if err != nil {
return nil, err
}
return &UploadFile{FileReaderCloser: fd, FileName: fileName}, nil
}
// UploadFileFromGlob 根据Glob从本地文件获取上传文件
func UploadFileFromGlob(glob string) ([]*UploadFile, error) {
files, err := filepath.Glob(glob)
if err != nil {
return nil, err
}
if len(files) == 0 {
log.Println("UploadFileFromGlob: len(files) == 0")
}
var ufiles []*UploadFile
for _, f := range files {
if s, err := os.Stat(f); err != nil || s.IsDir() {
continue
}
fd, err := os.Open(f)
if err != nil {
log.Println(fd.Name(), err)
} else {
ufiles = append(ufiles, &UploadFile{FileReaderCloser: fd, FileName: filepath.Base(fd.Name())})
}
}
return ufiles, nil
}

71
workflow.go Normal file
View File

@ -0,0 +1,71 @@
package requests
import "net/url"
// Workflow 工作流
type Workflow struct {
session *Session
URL string
Method string
Body *Params
Query map[string][]string
}
// NewWorkflow new and init workflow
func NewWorkflow(ses *Session) *Workflow {
wf := &Workflow{}
wf.SwitchSession(ses)
return wf
}
// SwitchSession 替换Session
func (wf *Workflow) SwitchSession(ses *Session) {
wf.session = ses
}
// SetParams 参数设置
func (wf *Workflow) SetParams(params ...interface{}) *Workflow {
plen := len(params)
defaultContentType := TypeURLENCODED
if plen >= 2 {
t := params[plen-1]
defaultContentType = t.(string)
wf.Body.ContentType = defaultContentType
} else {
wf.Body.ContentType = defaultContentType
}
if defaultContentType == TypeFormData {
// TODO: form-data
createMultipart(wf.Body, params)
} else {
var values url.Values
switch param := params[0].(type) {
case map[string]string:
values := make(url.Values)
for k, v := range param {
values.Set(k, v)
}
wf.Body.Body = []byte(values.Encode())
case map[string][]string:
values = param
wf.Body.Body = []byte(values.Encode())
case string:
wf.Body.Body = []byte(param)
case []byte:
wf.Body.Body = param
}
}
return wf
}
// Execute 执行
func (wf *Workflow) Execute() (*Response, error) {
req := buildBodyRequest(wf.Method, wf.URL, wf.Body)
resp, err := wf.session.client.Do(req)
if err != nil {
return nil, err
}
return FromHTTPResponse(resp)
}