A Go framework for building JSON web services inspired by Dropwizard
Go Shell
Latest commit 1199db5 Jun 4, 2016 @mihasya mihasya Merge pull request #94 from snikch/metrics-status-422
Allow 422 status counters
Permalink
Failed to load latest commit information.
example Example use of a metrics.Registry's JSON output Jun 24, 2014
mocking Better error messages for the mocking.URL panics Mar 6, 2014
.gitignore Fix .gitignore. Apr 29, 2013
.travis.yml Test with 1.5 version of Go. Jan 20, 2016
LICENSE Initial import. Feb 9, 2013
README.md Fix broken dropwizard link (-> dropwizard.io) Jun 1, 2016
base62.go go fmt Aug 5, 2013
bootstrap.sh Initial import. Feb 9, 2013
cache.go Allow overriding the cache headers from the underlying function. Apr 30, 2014
cache_test.go Allow overriding the cache headers from the underlying function. Apr 30, 2014
config.go export ConfigExt and change the registration function to be RegisterC… May 8, 2014
config_test.go (#23) File extension-specific configuration. Sep 14, 2013
config_test.json (#23) File extension-specific configuration. Sep 14, 2013
context.go go fmt Oct 4, 2013
context_test.go Fix test with dependence on goroutine scheduling. Jan 20, 2016
cors.go Add Access-Control-Allow-Credentials Sep 14, 2014
cors_test.go Add Access-Control-Allow-Credentials Sep 14, 2014
error.go Allow Marshaled to respond with arbitrary streams. Jan 21, 2016
error_test.go Allow Marshaled to respond with arbitrary streams. Jan 21, 2016
host_serve_mux.go Merge remote-tracking branch 'bigkevmcd/hostmux-with-port' May 2, 2014
host_serve_mux_test.go Merge remote-tracking branch 'bigkevmcd/hostmux-with-port' May 2, 2014
http_basic_auth.go Add HTTPBasicAuthFunc for custom credentials. Jan 17, 2014
http_basic_auth_test.go s/Status/StatusCode/ to be like the standard library. Jan 5, 2014
http_equiv_error.go s/Status/StatusCode/ to be like the standard library. Jan 5, 2014
json_logger.go Move the RequestIDCreator call before the ServeHTTP call Jul 17, 2015
json_logger_test.go Logging: put logger behind an interface Jul 10, 2014
logger.go Allow Marshaled to respond with arbitrary streams. Jan 21, 2016
logger_test.go fix test that relied on map iteration order Jul 22, 2015
marshaler.go Allow Marshaled to respond with arbitrary streams. Jan 21, 2016
marshaler_test.go Allow Marshaled to respond with arbitrary streams. Jan 21, 2016
method_not_allowed.go Added ResponseErrorWriter interface to allow for custom error json ma… Jul 27, 2015
metrics.go Update metrics.go May 26, 2016
metrics_test.go Refactored implementation of CounterByStatus(XX). Jan 3, 2014
middleware.go Use common ResponseErrorWriter interface Jul 24, 2015
middleware_test.go s/Status/StatusCode/ to be like the standard library. Jan 5, 2014
named_error.go Add NamedError for fully custom error responses. Aug 13, 2013
net.go gofmt Jul 22, 2015
not_found.go Added ResponseErrorWriter interface to allow for custom error json ma… Jul 27, 2015
post_processor.go Refactor PostProcessor to use TeeResponseWriter. Jan 5, 2014
post_processor_test.go Refactor PostProcessor to use TeeResponseWriter. Jan 5, 2014
server.go Merge remote-tracking branch 'origin/go1.3' Oct 5, 2014
server_test.go Use Go 1.3 http.Server.ConnState for graceful stop. Feb 28, 2014
tee.go Go doc for Flush. Apr 24, 2014
tee_test.go s/Status/StatusCode/ to be like the standard library. Jan 5, 2014
test.cnf Notes on the test OpenSSL config. Nov 16, 2013
test.crt Test SSL with our own cert, not Betable's. Nov 16, 2013
test.key Test SSL with our own cert, not Betable's. Nov 16, 2013
test_response_writer.go s/Status/StatusCode/ to be like the standard library. Jan 5, 2014
tigertonic.go Package comment and links. Jun 26, 2013
trie_serve_mux.go Params from wildcards trump params from query string May 7, 2014
trie_serve_mux_test.go Params from wildcards trump params from query string May 7, 2014
validate.sh initial travis CI support Jul 22, 2015
version.go Add Version handler for reporting version strings. Feb 14, 2014
version_test.go Add Version handler for reporting version strings. Feb 14, 2014

README.md

Tiger Tonic

Build Status

A Go framework for building JSON web services inspired by Dropwizard. If HTML is your game, this will hurt a little.

Like the Go language itself, Tiger Tonic strives to keep features orthogonal. It defers what it can to the Go standard library and a few other packages.

Documentation

Articles and talks

Reference

http://godoc.org/github.com/rcrowley/go-tigertonic

Community

Synopsis

tigertonic.TrieServeMux

HTTP routing in the Go standard library is pretty anemic. Enter tigertonic.TrieServeMux. It accepts an HTTP method, a URL pattern, and an http.Handler or an http.HandlerFunc. Components in the URL pattern wrapped in curly braces - { and } - are wildcards: their values (which don't cross slashes) are added to the URL as u.Query().Get("name").

HandleNamespace is like Handle but additionally strips the namespace from the URL, making API versioning, multitenant services, and relative links easier to manage. This is roughly equivalent to http.ServeMux's behavior.

tigertonic.HostServeMux

Use tigertonic.HostServeMux to serve multiple domain names from the same net.Listener.

tigertonic.Marshaled

Wrap a function in tigertonic.Marshaled to turn it into an http.Handler. The function signature must be something like this or tigertonic.Marshaled will panic:

func myHandler(*url.URL, http.Header, *MyRequest) (int, http.Header, *MyResponse, error)

Request bodies will be unmarshaled into a MyRequest struct and response bodies will be marshaled from MyResponse structs.

Should you need to respond with an error, the tigertonic.HTTPEquivError interface is implemented by tigertonic.BadRequest (and so on for every other HTTP response status) that can be wrapped around any error:

func myHandler(*url.URL, http.Header, *MyRequest) (int, http.Header, *MyResponse, error) {
    return 0, nil, nil, tigertonic.BadRequest{errors.New("Bad Request")}
}

Alternatively, you can return a valid status as the first output parameter and an error as the last; that status will be used in the error response.

If the return type of a tigertonic.Marshaled handler interface implements the io.Reader interface the stream will be written directly to the requestor. A Content-Type header is required to be specified in the response headers and the Accept header for these particular requests can be anything.

Additionally, if the return type of the tigertonic.Marshaled handler implements the io.Closer interface the stream will be automatically closed after it is flushed to the requestor.

tigertonic.Logged, tigertonic.JSONLogged, and tigertonic.ApacheLogged

Wrap an http.Handler in tigertonic.Logged to have the request and response headers and bodies logged to standard output. The second argument is an optional func(string) string called as requests and responses are logged to give the caller the opportunity to redact sensitive information from log entries.

Wrap an http.Handler in tigertonic.JSONLogged to have the request and response headers and bodies logged to standard output as JSON suitable for sending to ElasticSearch, Flume, Logstash, and so on. The JSON will be prefixed with @json:. The second argument is an optional func(string) string called as requests and responses are logged to give the caller the opportunity to redact sensitive information from log entries.

Wrap an http.Handler in tigertonic.ApacheLogged to have the request and response logged in the more traditional Apache combined log format.

tigertonic.Counted and tigertonic.Timed

Wrap an http.Handler in tigertonic.Counted or tigertonic.Timed to have the request counted or timed with go-metrics.

tigertonic.CountedByStatus and tigertonic.CountedByStatusXX

Wrap an http.Handler in tigertonic.CountedByStatus or tigertonic.CountedByStatusXX to have the response counted with go-metrics with a metrics.Counter for each HTTP status code or family of status codes (1xx, 2xx, and so on).

tigertonic.First

Call tigertonic.First with a variadic slice of http.Handlers. It will call ServeHTTP on each in succession until the first one that calls w.WriteHeader.

tigertonic.If

tigertonic.If expresses the most common use of tigertonic.First more naturally. Call tigertonic.If with a func(*http.Request) (http.Header, error) and an http.Handler. It will conditionally call the handler unless the function returns an error. In that case, the error is used to create a response.

tigertonic.PostProcessed and tigertonic.TeeResponseWriter

tigertonic.PostProcessed uses a tigertonic.TeeResponseWriter to record the response and call a func(*http.Request, *http.Response) after the response is written to the client to allow post-processing requests and responses.

tigertonic.HTTPBasicAuth

Wrap an http.Handler in tigertonic.HTTPBasicAuth, providing a map[string]string of authorized usernames to passwords, to require the request include a valid Authorization header.

tigertonic.CORSHandler and tigertonic.CORSBuilder

Wrap an http.Handler in tigertonic.CORSHandler (using CORSBuilder.Build()) to inject CORS-related headers. Currently only Origin-related headers (used for cross-origin browser requests) are supported.

tigertonic.Configure

Call tigertonic.Configure to read and unmarshal a JSON configuration file into a configuration structure of your own design. This is mere convenience and what you do with it after is up to you.

tigertonic.WithContext and tigertonic.Context

Wrap an http.Handler and a zero value of any non-interface type in tigertonic.WithContext to enable per-request context. Each request may call tigertonic.Context with the *http.Request in progress to get a pointer to the context which is of the type passed to tigertonic.WithContext.

tigertonic.Version

Respond with a version string that may be set at compile-time.

Usage

Install dependencies:

sh bootstrap.sh

Then define your service. The working example may be a more convenient place to start.

Requests that have bodies have types. JSON is deserialized by adding tigertonic.Marshaled to your routes.

type MyRequest struct {
	ID     string      `json:"id"`
	Stuff  interface{} `json:"stuff"`
}

Responses, too, have types. JSON is serialized by adding tigertonic.Marshaled to your routes.

type MyResponse struct {
	ID     string      `json:"id"`
	Stuff  interface{} `json:"stuff"`
}

Routes are just functions with a particular signature. You control the request and response types.

func myHandler(u *url.URL, h http.Header, *MyRequest) (int, http.Header, *MyResponse, error) {
    return http.StatusOK, nil, &MyResponse{"ID", "STUFF"}, nil
}

Wire it all up in main.main!

mux := tigertonic.NewTrieServeMux()
mux.Handle("POST", "/stuff", tigertonic.Timed(tigertonic.Marshaled(myHandler), "myHandler", nil))
tigertonic.NewServer(":8000", tigertonic.Logged(mux, nil)).ListenAndServe()

Ready for more? See the full example which includes all of these handlers plus an example of how to use tigertonic.Server to stop gracefully. Build it with go build, run it with ./example, and test it out:

curl -H"Host: example.com" -sv "http://127.0.0.1:8000/1.0/stuff/ID"
curl -H"Host: example.com" -X"POST" -d'{"id":"ID","stuff":"STUFF"}' -sv "http://127.0.0.1:8000/1.0/stuff"
curl -H"Host: example.com" -X"POST" -d'{"id":"ID","stuff":"STUFF"}' -sv "http://127.0.0.1:8000/1.0/stuff/ID"
curl -H"Host: example.com" -sv "http://127.0.0.1:8000/1.0/forbidden"

WTF?

Dropwizard was named after http://gunshowcomic.com/316 so Tiger Tonic was named after http://gunshowcomic.com/338.

If Tiger Tonic isn't your cup of tea, perhaps one of these fine tools suits you: