Skip to content

Commit

Permalink
feat: refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
fanjindong committed Aug 30, 2021
1 parent f412f33 commit a63749d
Show file tree
Hide file tree
Showing 13 changed files with 575 additions and 584 deletions.
65 changes: 25 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Requests

![](./images/TrakaiLithuania_ZH-CN0447602818_1920x1080.jpg)

Requests is an elegant and simple HTTP library for Go.
Expand All @@ -13,15 +14,16 @@ go get github.com/fanjindong/go-requests

### Make a Request

Making a request with Requests is very simple.
For this example:
Making a request with Requests is very simple. For this example:

```go
resp, err := requests.Get("https://example.com/ping", requests.Params{"key": "value"})
```

Now, we have a Response object called resp. We can get all the information we need from this object.

Requests’ simple API means that all forms of HTTP request are as obvious. For example, this is how you make an HTTP POST request:
Requests’ simple API means that all forms of HTTP request are as obvious. For example, this is how you make an HTTP POST
request:

```go
resp, err := requests.Post("https://example.com/ping", requests.Params{"k": "v"}, requests.Json{"key": "value"})
Expand All @@ -30,25 +32,27 @@ resp, err := requests.Post("https://example.com/ping", requests.Params{"k": "v"}
What about the other HTTP request types: PUT, DELETE, HEAD and OPTIONS? These are all just as simple:

```go
resp, err := requests.Put("https://example.com/ping", requests.Data{"key": "value"})
resp, err := requests.Put("https://example.com/ping", requests.Form{"key": "value"})
resp, err := requests.Delete("https://example.com/ping")
resp, err := requests.Head("https://example.com/ping")
resp, err := requests.Options("https://example.com/ping")
```

That’s all well and good, but it’s also only the start of what Requests can do.

### Passing Parameters In URLs

You often want to send some sort of data in the URL’s query string.
If you were constructing the URL by hand, this data would be given as key/value pairs in the URL after a question mark,
e.g. example.com/get?key=val. Requests allows you to provide these arguments as a dictionary of strings,
using the params keyword argument. As an example, if you wanted to pass key1=value1 and key2=value2 to example.com/get,
you would use the following code:
You often want to send some sort of data in the URL’s query string. If you were constructing the URL by hand, this data
would be given as key/value pairs in the URL after a question mark, e.g. example.com/get?key=val. Requests allows you to
provide these arguments as a dictionary of strings, using the params keyword argument. As an example, if you wanted to
pass key1=value1 and key2=value2 to example.com/get, you would use the following code:

```go
resp, err := requests.Get("https://example.com/get", requests.Params{"key1": "value1", "key2": "value2"})
```

You can see that the URL has been correctly encoded by printing the URL:

```go
fmt.Println(resp.Request.URL)
//https://example.com/get?key2=value2&key1=value1
Expand All @@ -70,8 +74,8 @@ There’s also a builtin JSON decoder, in case you’re dealing with JSON data:

```go
var rStruct struct{
Code int `json:"code"`
Message string `json:"message"`
Code int `json:"code"`
Message string `json:"message"`
}

err := resp.Json(&rStruct)
Expand All @@ -91,33 +95,24 @@ r, err := requests.Get("https://api.github.com/some/endpoint", requests.Headers{

### More complicated POST requests

Typically, you want to send some form-encoded data — much like an HTML form. To do this,
simply pass a `requests.Data` to the data argument.
Your data will automatically be form-encoded when the request is made:
Typically, you want to send some form-encoded data — much like an HTML form. To do this, simply pass a `requests.Form`
to the data argument. Your data will automatically be form-encoded when the request is made:

```go
r, err := requests.Post("https://httpbin.org/post", requests.Data{"key1": "value1", "key2": "value2"})
fmt.Println(r.Text)
r, err := requests.Post("https://httpbin.org/post", requests.Form{"key1": "value1", "key2": "value2"})
fmt.Println(r.Text())
//{"code":0,"message":"pong"}
```

For example, the GitHub API v3 accepts JSON-Encoded POST/PATCH data,
you can also pass it `requests.Json` using the json parameter and it will be encoded automatically:
For example, the GitHub API v3 accepts JSON-Encoded POST/PATCH data, you can also pass it `requests.Json` using the json
parameter and it will be encoded automatically:

```go
r, err := requests.Post("https://api.github.com/some/endpoint", requests.Json{"key1": "value1", "key2": "value2"})
```

Using the `requests.Json` in the request will change the Content-Type in the header to application/json.

### POST a Multipart-Encoded File

```go
file, err := requests.FileFromPath("demo.text")

r, err := requests.Post("https://httpbin.org/post", requests.Files{"key": "value", "file": file})
```

### Response Status Codes

We can check the response status code:
Expand All @@ -128,29 +123,19 @@ fmt.Println(r.StatusCode)
// 200
```

### Response Headers
### Response Header

We can view the server’s response headers:
We can view the server’s response header:

```go
fmt.Println(r.Headers)
fmt.Println(r.Header)
//map[Cache-Control:[private] Content-Type:[application/json] Set-Cookie:[QINGCLOUDELB=d9a2454c187d2875afb6701eb80e9c8761ebcf3b54797eae61b25b90f71273ea; path=/; HttpOnly]]

```

We can access the headers using Get method:

```go
r.Headers.Get("Content-Type")
//"application/json"
```

### Timeouts

You can tell Requests to stop waiting for a response after a given number of seconds with the timeout parameter.
Nearly all production code should use this parameter in nearly all requests.
Failure to do so can cause your program to hang indefinitely:


```go
r, err := requests.Get("https://github.com/", requests.Params{"key": "value"}, requests.Timeout(3*time.Secend))
```
120 changes: 120 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package requests

import (
"net/http"
"strings"
)

const (
version = "0.0.1"
userAgent = "go-requests/" + version
author = "fanjindong"
)

const (
GET = "GET"
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
OPTIONS = "OPTIONS"
PATCH = "PATCH"
HEAD = "HEAD"
)

func Get(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(GET, url, opts...)
}

func Post(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(POST, url, opts...)
}

func Put(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(PUT, url, opts...)
}

func Delete(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(DELETE, url, opts...)
}

func ReqOptions(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(OPTIONS, url, opts...)
}

func Patch(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(PATCH, url, opts...)
}

func Head(url string, opts ...ReqOption) (*Response, error) {
return DefaultClient.Request(HEAD, url, opts...)
}

type Client struct {
*http.Client
}

var DefaultClient = &Client{http.DefaultClient}

func NewClient(opts ...ClientOption) *Client {
c := &Client{&http.Client{}}
for _, opt := range opts {
opt(c)
}
return c
}

func (s *Client) Request(method, url string, opts ...ReqOption) (*Response, error) {
method = strings.ToUpper(method)
switch method {
case HEAD, GET, POST, DELETE, OPTIONS, PUT, PATCH:
default:
return nil, ErrInvalidMethod
}

req, err := NewRequest(method, url)
if err != nil {
return nil, err
}

for _, opt := range opts {
err = opt.Do(req)
if err != nil {
return nil, err
}
}

resp, err := s.Do(req.Request)
if err != nil {
return nil, err
}

return NewResponse(resp)
}

func (s *Client) Get(url string, opts ...ReqOption) (*Response, error) {
return s.Request(GET, url, opts...)
}

func (s *Client) Post(url string, opts ...ReqOption) (*Response, error) {
return s.Request(POST, url, opts...)
}

func (s *Client) Put(url string, opts ...ReqOption) (*Response, error) {
return s.Request(PUT, url, opts...)
}

func (s *Client) Delete(url string, opts ...ReqOption) (*Response, error) {
return s.Request(DELETE, url, opts...)
}

func (s *Client) ReqOptions(url string, opts ...ReqOption) (*Response, error) {
return s.Request(OPTIONS, url, opts...)
}

func (s *Client) Patch(url string, opts ...ReqOption) (*Response, error) {
return s.Request(PATCH, url, opts...)
}

func (s *Client) Head(url string, opts ...ReqOption) (*Response, error) {
return s.Request(HEAD, url, opts...)
}
109 changes: 109 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package requests

import (
"fmt"
"reflect"
"testing"
"time"
)

var testUrl = fmt.Sprintf("http://127.0.0.1:%d", port)

func TestGet(t *testing.T) {
type args struct {
url string
option []ReqOption
}
tests := []struct {
name string
args args
want map[string]string
wantErr bool
}{
{name: "1", args: args{url: testUrl + "/get", option: []ReqOption{}}, want: map[string]string{}},
{name: "2", args: args{url: testUrl + "/get", option: []ReqOption{Params{"a": "1"}}}, want: map[string]string{"a": "1"}},
{name: "3", args: args{url: testUrl + "/get", option: []ReqOption{Params{"a": "1", "b": "x"}}}, want: map[string]string{"a": "1", "b": "x"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp, err := Get(tt.args.url, tt.args.option...)
if (err != nil) != tt.wantErr {
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
return
}
got := map[string]string{}
if err := resp.Json(&got); err != nil {
t.Errorf("resp.Json() error = %v", err)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Get() got = %v, want %v", got, tt.want)
}
})
}
}

func TestPost(t *testing.T) {
type args struct {
url string
option []ReqOption
}
tests := []struct {
name string
args args
want map[string]interface{}
wantErr bool
}{
{name: "1", args: args{url: testUrl + "/post", option: []ReqOption{}}, want: map[string]interface{}{}},
{name: "2", args: args{url: testUrl + "/post", option: []ReqOption{Json{"a": "1"}}}, want: map[string]interface{}{"a": "1"}},
{name: "3", args: args{url: testUrl + "/post", option: []ReqOption{Json{"a": "x", "b": 1.2}}}, want: map[string]interface{}{"a": "x", "b": 1.2}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp, err := Post(tt.args.url, tt.args.option...)
if (err != nil) != tt.wantErr {
t.Errorf("Post() error = %v, wantErr %v", err, tt.wantErr)
return
}
got := map[string]interface{}{}
if err := resp.Json(&got); err != nil {
t.Errorf("resp.Json() error = %v", err)
return
}
//for k := range got {
// if got[k] != tt.want[k] {
// t.Errorf("Post() k = %v, got = %+v, want %+v", k, got[k].(int), tt.want[k].(int))
// }
//}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Post() got = %+v, want %+v", got, tt.want)
}
})
}
}

func TestReuseConnection(t *testing.T) {
for i := 0; i < 10; i++ {
resp, err := Get(testUrl)
//resp, err := http.Get(testUrl)
if err != nil {
t.Log(err)
return
}
//data, _ := ioutil.ReadAll(resp.Body)
t.Log(resp.Status, resp.Text())
//resp.Body.Close()
time.Sleep(1000 * time.Millisecond)
}
}

//BenchmarkGetRequest-8 27895 43212 ns/op
func BenchmarkGetRequest(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := Get(testUrl)
if err != nil {
panic(err)
}
//resp.Text()
}
}
4 changes: 2 additions & 2 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package requests
import "github.com/pkg/errors"

var (
// ErrInvalidJson will be throw out when request form body data can not be Marshal
// ErrInvalidForm will be throw out when request form body data can not be Marshal
ErrInvalidForm = errors.New("go-requests: Invalid Form value")

// ErrInvalidJson will be throw out when request json body data can not be Marshal
ErrInvalidJson = errors.New("go-requests: Invalid Json value")

// ErrUnrecognizedEncoding will be throw out while changing response encoding
// if encoding is not recognized
ErrUnrecognizedEncoding = errors.New("go-requests: Unrecognized encoding")
//ErrUnrecognizedEncoding = errors.New("go-requests: Unrecognized encoding")

// ErrInvalidMethod will be throw out when method not in
// [HEAD, GET, POST, DELETE, OPTIONS, PUT, PATCH, CONNECT, TRACE]
Expand Down
Loading

0 comments on commit a63749d

Please sign in to comment.