445 lines
11 KiB
Markdown
445 lines
11 KiB
Markdown
|
[![Build Status](https://img.shields.io/travis/franela/goreq/master.svg)](https://travis-ci.org/franela/goreq)
|
||
|
[![GoDoc](https://godoc.org/github.com/franela/goreq?status.svg)](https://godoc.org/github.com/franela/goreq)
|
||
|
|
||
|
GoReq
|
||
|
=======
|
||
|
|
||
|
Simple and sane HTTP request library for Go language.
|
||
|
|
||
|
|
||
|
|
||
|
**Table of Contents**
|
||
|
|
||
|
- [Why GoReq?](#user-content-why-goreq)
|
||
|
- [How do I install it?](#user-content-how-do-i-install-it)
|
||
|
- [What can I do with it?](#user-content-what-can-i-do-with-it)
|
||
|
- [Making requests with different methods](#user-content-making-requests-with-different-methods)
|
||
|
- [GET](#user-content-get)
|
||
|
- [Tags](#user-content-tags)
|
||
|
- [POST](#user-content-post)
|
||
|
- [Sending payloads in the Body](#user-content-sending-payloads-in-the-body)
|
||
|
- [Specifiying request headers](#user-content-specifiying-request-headers)
|
||
|
- [Sending Cookies](#cookie-support)
|
||
|
- [Setting timeouts](#user-content-setting-timeouts)
|
||
|
- [Using the Response and Error](#user-content-using-the-response-and-error)
|
||
|
- [Receiving JSON](#user-content-receiving-json)
|
||
|
- [Sending/Receiving Compressed Payloads](#user-content-sendingreceiving-compressed-payloads)
|
||
|
- [Using gzip compression:](#user-content-using-gzip-compression)
|
||
|
- [Using deflate compression:](#user-content-using-deflate-compression)
|
||
|
- [Using compressed responses:](#user-content-using-compressed-responses)
|
||
|
- [Proxy](#proxy)
|
||
|
- [Debugging requests](#debug)
|
||
|
- [Getting raw Request & Response](#getting-raw-request--response)
|
||
|
- [TODO:](#user-content-todo)
|
||
|
|
||
|
|
||
|
|
||
|
Why GoReq?
|
||
|
==========
|
||
|
|
||
|
Go has very nice native libraries that allows you to do lots of cool things. But sometimes those libraries are too low level, which means that to do a simple thing, like an HTTP Request, it takes some time. And if you want to do something as simple as adding a timeout to a request, you will end up writing several lines of code.
|
||
|
|
||
|
This is why we think GoReq is useful. Because you can do all your HTTP requests in a very simple and comprehensive way, while enabling you to do more advanced stuff by giving you access to the native API.
|
||
|
|
||
|
How do I install it?
|
||
|
====================
|
||
|
|
||
|
```bash
|
||
|
go get github.com/franela/goreq
|
||
|
```
|
||
|
|
||
|
What can I do with it?
|
||
|
======================
|
||
|
|
||
|
## Making requests with different methods
|
||
|
|
||
|
#### GET
|
||
|
```go
|
||
|
res, err := goreq.Request{ Uri: "http://www.google.com" }.Do()
|
||
|
```
|
||
|
|
||
|
GoReq default method is GET.
|
||
|
|
||
|
You can also set value to GET method easily
|
||
|
|
||
|
```go
|
||
|
type Item struct {
|
||
|
Limit int
|
||
|
Skip int
|
||
|
Fields string
|
||
|
}
|
||
|
|
||
|
item := Item {
|
||
|
Limit: 3,
|
||
|
Skip: 5,
|
||
|
Fields: "Value",
|
||
|
}
|
||
|
|
||
|
res, err := goreq.Request{
|
||
|
Uri: "http://localhost:3000/",
|
||
|
QueryString: item,
|
||
|
}.Do()
|
||
|
```
|
||
|
The sample above will send `http://localhost:3000/?limit=3&skip=5&fields=Value`
|
||
|
|
||
|
Alternatively the `url` tag can be used in struct fields to customize encoding properties
|
||
|
|
||
|
```go
|
||
|
type Item struct {
|
||
|
TheLimit int `url:"the_limit"`
|
||
|
TheSkip string `url:"the_skip,omitempty"`
|
||
|
TheFields string `url:"-"`
|
||
|
}
|
||
|
|
||
|
item := Item {
|
||
|
TheLimit: 3,
|
||
|
TheSkip: "",
|
||
|
TheFields: "Value",
|
||
|
}
|
||
|
|
||
|
res, err := goreq.Request{
|
||
|
Uri: "http://localhost:3000/",
|
||
|
QueryString: item,
|
||
|
}.Do()
|
||
|
```
|
||
|
The sample above will send `http://localhost:3000/?the_limit=3`
|
||
|
|
||
|
|
||
|
QueryString also support url.Values
|
||
|
|
||
|
```go
|
||
|
item := url.Values{}
|
||
|
item.Set("Limit", 3)
|
||
|
item.Add("Field", "somefield")
|
||
|
item.Add("Field", "someotherfield")
|
||
|
|
||
|
res, err := goreq.Request{
|
||
|
Uri: "http://localhost:3000/",
|
||
|
QueryString: item,
|
||
|
}.Do()
|
||
|
```
|
||
|
|
||
|
The sample above will send `http://localhost:3000/?limit=3&field=somefield&field=someotherfield`
|
||
|
|
||
|
### Tags
|
||
|
|
||
|
Struct field `url` tag is mainly used as the request parameter name.
|
||
|
Tags can be comma separated multiple values, 1st value is for naming and rest has special meanings.
|
||
|
|
||
|
- special tag for 1st value
|
||
|
- `-`: value is ignored if set this
|
||
|
|
||
|
- special tag for rest 2nd value
|
||
|
- `omitempty`: zero-value is ignored if set this
|
||
|
- `squash`: the fields of embedded struct is used for parameter
|
||
|
|
||
|
#### Tag Examples
|
||
|
|
||
|
```go
|
||
|
type Place struct {
|
||
|
Country string `url:"country"`
|
||
|
City string `url:"city"`
|
||
|
ZipCode string `url:"zipcode,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Person struct {
|
||
|
Place `url:",squash"`
|
||
|
|
||
|
FirstName string `url:"first_name"`
|
||
|
LastName string `url:"last_name"`
|
||
|
Age string `url:"age,omitempty"`
|
||
|
Password string `url:"-"`
|
||
|
}
|
||
|
|
||
|
johnbull := Person{
|
||
|
Place: Place{ // squash the embedded struct value
|
||
|
Country: "UK",
|
||
|
City: "London",
|
||
|
ZipCode: "SW1",
|
||
|
},
|
||
|
FirstName: "John",
|
||
|
LastName: "Doe",
|
||
|
Age: "35",
|
||
|
Password: "my-secret", // ignored for parameter
|
||
|
}
|
||
|
|
||
|
goreq.Request{
|
||
|
Uri: "http://localhost/",
|
||
|
QueryString: johnbull,
|
||
|
}.Do()
|
||
|
// => `http://localhost/?first_name=John&last_name=Doe&age=35&country=UK&city=London&zip_code=SW1`
|
||
|
|
||
|
|
||
|
// age and zipcode will be ignored because of `omitempty`
|
||
|
// but firstname isn't.
|
||
|
samurai := Person{
|
||
|
Place: Place{ // squash the embedded struct value
|
||
|
Country: "Japan",
|
||
|
City: "Tokyo",
|
||
|
},
|
||
|
LastName: "Yagyu",
|
||
|
}
|
||
|
|
||
|
goreq.Request{
|
||
|
Uri: "http://localhost/",
|
||
|
QueryString: samurai,
|
||
|
}.Do()
|
||
|
// => `http://localhost/?first_name=&last_name=yagyu&country=Japan&city=Tokyo`
|
||
|
```
|
||
|
|
||
|
|
||
|
#### POST
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{ Method: "POST", Uri: "http://www.google.com" }.Do()
|
||
|
```
|
||
|
|
||
|
## Sending payloads in the Body
|
||
|
|
||
|
You can send ```string```, ```Reader``` or ```interface{}``` in the body. The first two will be sent as text. The last one will be marshalled to JSON, if possible.
|
||
|
|
||
|
```go
|
||
|
type Item struct {
|
||
|
Id int
|
||
|
Name string
|
||
|
}
|
||
|
|
||
|
item := Item{ Id: 1111, Name: "foobar" }
|
||
|
|
||
|
res, err := goreq.Request{
|
||
|
Method: "POST",
|
||
|
Uri: "http://www.google.com",
|
||
|
Body: item,
|
||
|
}.Do()
|
||
|
```
|
||
|
|
||
|
## Specifiying request headers
|
||
|
|
||
|
We think that most of the times the request headers that you use are: ```Host```, ```Content-Type```, ```Accept``` and ```User-Agent```. This is why we decided to make it very easy to set these headers.
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Uri: "http://www.google.com",
|
||
|
Host: "foobar.com",
|
||
|
Accept: "application/json",
|
||
|
ContentType: "application/json",
|
||
|
UserAgent: "goreq",
|
||
|
}.Do()
|
||
|
```
|
||
|
|
||
|
But sometimes you need to set other headers. You can still do it.
|
||
|
|
||
|
```go
|
||
|
req := goreq.Request{ Uri: "http://www.google.com" }
|
||
|
|
||
|
req.AddHeader("X-Custom", "somevalue")
|
||
|
|
||
|
req.Do()
|
||
|
```
|
||
|
|
||
|
Alternatively you can use the `WithHeader` function to keep the syntax short
|
||
|
|
||
|
```go
|
||
|
res, err = goreq.Request{ Uri: "http://www.google.com" }.WithHeader("X-Custom", "somevalue").Do()
|
||
|
```
|
||
|
|
||
|
## Cookie support
|
||
|
|
||
|
Cookies can be either set at the request level by sending a [CookieJar](http://golang.org/pkg/net/http/cookiejar/) in the `CookieJar` request field
|
||
|
or you can use goreq's one-liner WithCookie method as shown below
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Uri: "http://www.google.com",
|
||
|
}.
|
||
|
WithCookie(&http.Cookie{Name: "c1", Value: "v1"}).
|
||
|
Do()
|
||
|
```
|
||
|
|
||
|
## Setting timeouts
|
||
|
|
||
|
GoReq supports 2 kind of timeouts. A general connection timeout and a request specific one. By default the connection timeout is of 1 second. There is no default for request timeout, which means it will wait forever.
|
||
|
|
||
|
You can change the connection timeout doing:
|
||
|
|
||
|
```go
|
||
|
goreq.SetConnectTimeout(100 * time.Millisecond)
|
||
|
```
|
||
|
|
||
|
And specify the request timeout doing:
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Uri: "http://www.google.com",
|
||
|
Timeout: 500 * time.Millisecond,
|
||
|
}.Do()
|
||
|
```
|
||
|
|
||
|
## Using the Response and Error
|
||
|
|
||
|
GoReq will always return 2 values: a ```Response``` and an ```Error```.
|
||
|
If ```Error``` is not ```nil``` it means that an error happened while doing the request and you shouldn't use the ```Response``` in any way.
|
||
|
You can check what happened by getting the error message:
|
||
|
|
||
|
```go
|
||
|
fmt.Println(err.Error())
|
||
|
```
|
||
|
And to make it easy to know if it was a timeout error, you can ask the error or return it:
|
||
|
|
||
|
```go
|
||
|
if serr, ok := err.(*goreq.Error); ok {
|
||
|
if serr.Timeout() {
|
||
|
...
|
||
|
}
|
||
|
}
|
||
|
return err
|
||
|
```
|
||
|
|
||
|
If you don't get an error, you can safely use the ```Response```.
|
||
|
|
||
|
```go
|
||
|
res.Uri // return final URL location of the response (fulfilled after redirect was made)
|
||
|
res.StatusCode // return the status code of the response
|
||
|
res.Body // gives you access to the body
|
||
|
res.Body.ToString() // will return the body as a string
|
||
|
res.Header.Get("Content-Type") // gives you access to all the response headers
|
||
|
```
|
||
|
Remember that you should **always** close `res.Body` if it's not `nil`
|
||
|
|
||
|
## Receiving JSON
|
||
|
|
||
|
GoReq will help you to receive and unmarshal JSON.
|
||
|
|
||
|
```go
|
||
|
type Item struct {
|
||
|
Id int
|
||
|
Name string
|
||
|
}
|
||
|
|
||
|
var item Item
|
||
|
|
||
|
res.Body.FromJsonTo(&item)
|
||
|
```
|
||
|
|
||
|
## Sending/Receiving Compressed Payloads
|
||
|
GoReq supports gzip, deflate and zlib compression of requests' body and transparent decompression of responses provided they have a correct `Content-Encoding` header.
|
||
|
|
||
|
#####Using gzip compression:
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Method: "POST",
|
||
|
Uri: "http://www.google.com",
|
||
|
Body: item,
|
||
|
Compression: goreq.Gzip(),
|
||
|
}.Do()
|
||
|
```
|
||
|
#####Using deflate/zlib compression:
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Method: "POST",
|
||
|
Uri: "http://www.google.com",
|
||
|
Body: item,
|
||
|
Compression: goreq.Deflate(),
|
||
|
}.Do()
|
||
|
```
|
||
|
#####Using compressed responses:
|
||
|
If servers replies a correct and matching `Content-Encoding` header (gzip requires `Content-Encoding: gzip` and deflate `Content-Encoding: deflate`) goreq transparently decompresses the response so the previous example should always work:
|
||
|
```go
|
||
|
type Item struct {
|
||
|
Id int
|
||
|
Name string
|
||
|
}
|
||
|
res, err := goreq.Request{
|
||
|
Method: "POST",
|
||
|
Uri: "http://www.google.com",
|
||
|
Body: item,
|
||
|
Compression: goreq.Gzip(),
|
||
|
}.Do()
|
||
|
var item Item
|
||
|
res.Body.FromJsonTo(&item)
|
||
|
```
|
||
|
If no `Content-Encoding` header is replied by the server GoReq will return the crude response.
|
||
|
|
||
|
## Proxy
|
||
|
If you need to use a proxy for your requests GoReq supports the standard `http_proxy` env variable as well as manually setting the proxy for each request
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Method: "GET",
|
||
|
Proxy: "http://myproxy:myproxyport",
|
||
|
Uri: "http://www.google.com",
|
||
|
}.Do()
|
||
|
```
|
||
|
|
||
|
### Proxy basic auth is also supported
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Method: "GET",
|
||
|
Proxy: "http://user:pass@myproxy:myproxyport",
|
||
|
Uri: "http://www.google.com",
|
||
|
}.Do()
|
||
|
```
|
||
|
|
||
|
## Debug
|
||
|
If you need to debug your http requests, it can print the http request detail.
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Method: "GET",
|
||
|
Uri: "http://www.google.com",
|
||
|
Compression: goreq.Gzip(),
|
||
|
ShowDebug: true,
|
||
|
}.Do()
|
||
|
fmt.Println(res, err)
|
||
|
```
|
||
|
|
||
|
and it will print the log:
|
||
|
```
|
||
|
GET / HTTP/1.1
|
||
|
Host: www.google.com
|
||
|
Accept:
|
||
|
Accept-Encoding: gzip
|
||
|
Content-Encoding: gzip
|
||
|
Content-Type:
|
||
|
```
|
||
|
|
||
|
|
||
|
### Getting raw Request & Response
|
||
|
|
||
|
To get the Request:
|
||
|
|
||
|
```go
|
||
|
req := goreq.Request{
|
||
|
Host: "foobar.com",
|
||
|
}
|
||
|
|
||
|
//req.Request will return a new instance of an http.Request so you can safely use it for something else
|
||
|
request, _ := req.NewRequest()
|
||
|
|
||
|
```
|
||
|
|
||
|
|
||
|
To get the Response:
|
||
|
|
||
|
```go
|
||
|
res, err := goreq.Request{
|
||
|
Method: "GET",
|
||
|
Uri: "http://www.google.com",
|
||
|
Compression: goreq.Gzip(),
|
||
|
ShowDebug: true,
|
||
|
}.Do()
|
||
|
|
||
|
// res.Response will contain the original http.Response structure
|
||
|
fmt.Println(res.Response, err)
|
||
|
```
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
TODO:
|
||
|
-----
|
||
|
|
||
|
We do have a couple of [issues](https://github.com/franela/goreq/issues) pending we'll be addressing soon. But feel free to
|
||
|
contribute and send us PRs (with tests please :smile:).
|