rest

package module
v0.0.0-...-bb0c534 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 7, 2018 License: MIT Imports: 15 Imported by: 6

README

rest

This library contains a number of useful middlewares for writing a HTTP server in Go. For more information and package documentation, please see the godoc documentation.

Defining Custom Error Responses

rest exposes a number of HTTP error handlers - for example, rest.ServerError(w, r, err) will write a 500 server error to w. By default, these error handlers will write a generic JSON response over the wire, using fields specified by the HTTP problem spec.

You can define a custom error handler if you like (say if you want to return a HTML server error, or 404 error or similar) by calling RegisterHandler:

rest.RegisterHandler(500, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    err := rest.CtxErr(r)
    fmt.Println("Server error:", err)
    w.Header().Set("Content-Type", "text/html")
    w.WriteHeader(500)
    w.Write([]byte("<html><body>Server Error</body></html>"))
}))
Debugging

Set the DEBUG_HTTP_TRAFFIC environment variable to print out all request/response traffic being made by the client.

rest also includes a Transport that is a drop in for a http.Transport, but includes support for debugging HTTP requests. Add it like so:

client := http.Client{
    Transport: &rest.Transport{
        Debug: true,
        Output: os.Stderr,
        Transport: http.DefaultTransport,
    },
}

Documentation

Overview

Package rest implements responses and a HTTP client for API consumption.

Index

Examples

Constants

View Source
const Version = "1.3"

Variables

View Source
var Logger log.Logger = log.New()

Logger logs information about incoming requests.

Functions

func BadRequest

func BadRequest(w http.ResponseWriter, r *http.Request, err *Error)

BadRequest logs a 400 error and then returns a 400 response to the client.

func CtxDomain

func CtxDomain(r *http.Request) string

CtxDomain returns a domain that's been set on the request. Use it to get the domain set on a 401 error handler.

func CtxErr

func CtxErr(r *http.Request) error

CtxErr returns an error that's been stored in the Request context.

func DefaultErrorParser

func DefaultErrorParser(resp *http.Response) error

DefaultErrorParser attempts to parse the response body as a rest.Error. If it cannot do so, return an error containing the entire response body.

func Forbidden

func Forbidden(w http.ResponseWriter, r *http.Request, err *Error)

Forbidden returns a 403 Forbidden status code to the client, with the given Error object in the response body.

func Gone

func Gone(w http.ResponseWriter, r *http.Request)

Gone responds to the request with a 410 Gone error message

func NoContent

func NoContent(w http.ResponseWriter)

NoContent returns a 204 No Content message.

func NotAllowed

func NotAllowed(w http.ResponseWriter, r *http.Request)

NotAllowed returns a generic HTTP 405 Not Allowed status and response body to the client.

func NotFound

func NotFound(w http.ResponseWriter, r *http.Request)

NotFound returns a 404 Not Found error to the client.

func RegisterHandler

func RegisterHandler(code int, f http.Handler)

RegisterHandler registers the given HandlerFunc to serve HTTP requests for the given status code. Use CtxErr and CtxDomain to retrieve extra values set on the request in f (if any).

Despite registering the handler for the code, f is responsible for calling WriteHeader(code) since it may want to set response headers first.

To delete a Handler, call RegisterHandler with nil for the second argument.

Example
package main

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/Shyp/rest"
)

func main() {
	rest.RegisterHandler(500, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := rest.CtxErr(r)
		fmt.Println("Server error:", err)
		w.Header().Set("Content-Type", "text/html")
		w.WriteHeader(500)
		w.Write([]byte("<html><body>Server Error</body></html>"))
	}))

	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	rest.ServerError(w, req, errors.New("Something bad happened"))
}
Output:

Server error: Something bad happened

func ServerError

func ServerError(w http.ResponseWriter, r *http.Request, err error)

ServerError logs the error to the Logger, and then responds to the request with a generic 500 server error message. ServerError panics if err is nil.

func Unauthorized

func Unauthorized(w http.ResponseWriter, r *http.Request, domain string)

Unauthorized sets the Domain in the request context

Types

type Client

type Client struct {
	// Username for use in HTTP Basic Auth
	ID string
	// Password for use in HTTP Basic Auth
	Token string
	// HTTP Client to use for making requests
	Client *http.Client
	// The base URL for all requests to this API, for example,
	// "https://fax.twilio.com/v1"
	Base string
	// Set UploadType to JSON or FormURLEncoded to control how data is sent to
	// the server. Defaults to FormURLEncoded.
	UploadType UploadType
	// ErrorParser is invoked when the client gets a 400-or-higher status code
	// from the server. Defaults to rest.DefaultErrorParser.
	ErrorParser func(*http.Response) error
}

Client is a generic Rest client for making HTTP requests.

Example
package main

import (
	"fmt"

	"github.com/Shyp/rest"
)

func main() {
	client := rest.NewClient("jobs", "secretpassword", "http://ipinfo.io")
	req, _ := client.NewRequest("GET", "/json", nil)
	type resp struct {
		City string `json:"city"`
		Ip   string `json:"ip"`
	}
	var r resp
	client.Do(req, &r)
	fmt.Println(r.Ip)
}
Output:

func NewClient

func NewClient(user, pass, base string) *Client

NewClient returns a new Client with the given user and password. Base is the scheme+domain to hit for all requests.

Example
package main

import (
	"fmt"

	"github.com/Shyp/rest"
)

func main() {
	client := rest.NewClient("jobs", "secretpassword", "http://ipinfo.io")
	req, _ := client.NewRequest("GET", "/json", nil)
	type resp struct {
		City string `json:"city"`
		Ip   string `json:"ip"`
	}
	var r resp
	client.Do(req, &r)
	fmt.Println(r.Ip)
}
Output:

func (*Client) DialSocket

func (c *Client) DialSocket(socket string, transport *http.Transport)

DialSocket configures c to use the provided socket and http.Transport to dial a Unix socket instead of a TCP port.

If transport is nil, the settings from DefaultTransport are used.

func (*Client) Do

func (c *Client) Do(r *http.Request, v interface{}) error

Do performs the HTTP request. If the HTTP response is in the 2xx range, Unmarshal the response body into v. If the response status code is 400 or above, attempt to Unmarshal the response into an Error. Otherwise return a generic http error.

Example
package main

import (
	"fmt"

	"github.com/Shyp/rest"
)

func main() {
	client := rest.NewClient("jobs", "secretpassword", "http://ipinfo.io")
	req, _ := client.NewRequest("GET", "/json", nil)
	type resp struct {
		City string `json:"city"`
		Ip   string `json:"ip"`
	}
	var r resp
	client.Do(req, &r)
	fmt.Println(r.Ip)
}
Output:

func (*Client) NewRequest

func (c *Client) NewRequest(method, path string, body io.Reader) (*http.Request, error)

NewRequest creates a new Request and sets basic auth based on the client's authentication information.

Example
package main

import (
	"fmt"

	"github.com/Shyp/rest"
)

func main() {
	client := rest.NewClient("jobs", "secretpassword", "http://ipinfo.io")
	req, _ := client.NewRequest("GET", "/json", nil)
	type resp struct {
		City string `json:"city"`
		Ip   string `json:"ip"`
	}
	var r resp
	client.Do(req, &r)
	fmt.Println(r.Ip)
}
Output:

type Error

type Error struct {
	// The main error message. Should be short enough to fit in a phone's
	// alert box. Do not end this message with a period.
	Title string `json:"title"`

	// Id of this error message ("forbidden", "invalid_parameter", etc)
	ID string `json:"id"`

	// More information about what went wrong.
	Detail string `json:"detail,omitempty"`

	// Path to the object that's in error.
	Instance string `json:"instance,omitempty"`

	// Link to more information about the error (Zendesk, API docs, etc).
	Type string `json:"type,omitempty"`

	// HTTP status code of the error.
	Status int `json:"status,omitempty"`
}

Error implements the HTTP Problem spec laid out here: https://tools.ietf.org/html/rfc7807

func (*Error) Error

func (e *Error) Error() string

func (*Error) String

func (e *Error) String() string

type Transport

type Transport struct {
	// The underlying RoundTripper.
	RoundTripper http.RoundTripper
	// Whether to write the HTTP request and response contents to Output.
	Debug bool
	// If Debug is true, write the HTTP request and response contents here. If
	// Output is nil, os.Stderr will be used.
	Output io.Writer
}

Transport implements HTTP round trips, but adds hooks for debugging the HTTP request.

Example
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/Shyp/rest"
)

func main() {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello World"))
	}))
	defer server.Close()
	b := new(bytes.Buffer)
	client := http.Client{
		Transport: &rest.Transport{Debug: true, Output: b},
	}
	req, _ := http.NewRequest("GET", server.URL+"/bar", nil)
	client.Do(req)

	// Dump the HTTP request from the buffer, but skip the lines that change.
	scanner := bufio.NewScanner(b)
	for scanner.Scan() {
		text := scanner.Text()
		if strings.HasPrefix(text, "Host:") || strings.HasPrefix(text, "Date:") {
			continue
		}
		fmt.Println(text)
	}
}
Output:

GET /bar HTTP/1.1
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8

Hello World
var DefaultTransport *Transport

DefaultTransport is like http.DefaultTransport, but prints the contents of HTTP requests to os.Stderr if the DEBUG_HTTP_TRAFFIC environment variable is set to true.

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

type UploadType

type UploadType string
var FormURLEncoded UploadType = "application/x-www-form-urlencoded"

FormURLEncoded specifies you'd like to upload form-urlencoded data.

var JSON UploadType = "application/json"

JSON specifies you'd like to upload JSON data.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL