troutdarlinggo/trout on Github

Importing trout

import "darlinggo.co/trout"

About trout

trout is an opinionated router that is biased towards RESTful services. It does not rely on global, mutable state and has no dependencies. trout also goes out of its way to enable servers to correctly respond with an http.StatusMethodNotAllowed error instead of an http.StatusNotFound error when the endpoint can be matched but is not configured to respond to the HTTP method the request used. It also takes pains to make sure that OPTIONS requests can be fulfilled, by making the methods an endpoint is configured with available to the http.Handler. In general, the over-arching goals of trout are:

Docs can be found on GoDoc.org.

If you’re using trout, we encourage you to join the trout mailing list, which will be our main mode of communication.

Using trout

Creating a router

Creating a router is straight-forward: the trout.Router type’s zero value is an acceptable router with no endpoints. Adding endpoints is a matter of calling methods on the variable.

var router trout.Router
router.Endpoint("/posts/{slug}/comments/{id}").Handler(postsHandler)

That routemr will now match the URL /posts/WHATEVERYOUTYPE/comments/123. You can replace WHATEVERYOUTYPE and 123 with any string that doesn’t contain a /. All requests matching this pattern will be handled by postsHandler.

The Endpoint method is basic: it accepts a string to match the URL against. Strings get broken down into resources; resources are split by the / character. Resources come in two flavours: static and dynamic. A static resource will match the resource text exactly; posts and comments in the example above are static resources. Dynamic resources are just placeholders; they match any text at all; WHATEVERYOUTYPE and 123 are dynamic resources in the example above.

The Endpoint method returns a trout.Endpoint, which can have an http.Handler associated with it by calling its Handler method, and passing the http.Handler you want to use as the handler for requests that match the endpoint.

Working with HTTP methods

var router trout.Router
router.Endpoint("/posts/{slug}").Methods("GET", "POST").Handler(postsHandler)

The example above associates postsHandler with the /posts/{slug} endpoint, but only for requests made using the GET or POST HTTP method. All other requests will return an http.StatusMethodNotAllowed error. Any number of methods can be passed to the Methods… errr… method.

Working with variables

Now that a handler has been matched, we need to get the values that filled the dynamic resources placeholders in the URL. The trout.RequestVars helper function can be used to return the values the were used.

Variables in trout are passed as request headers. All the trout parameters are set as Trout-Param-RESOURCETEXT, where RESOURCETEXT is the text you entered between { and } in the endpoint. For example, /posts/{slug}/comments/{id} would have Trout-Param-Slug and Trout-Param-Id set in the request headers. trout.RequestVars(r) simply returns all headers that begin with Trout-Param-, and strip that prefix, returning an http.Header object. So calling trout.RequestVars(r) in our example would return an http.Header object with keys for Id and Slug.

In the event that the same text is reused as a dynamic resource in multiple parts of the endpoint, both values will still be available, because each key in an http.Header corresponds to a slice of values. For example, if the endpoint is /posts/{id}/comments/{id}, the http.Header returned from trout.RequestVars(r) will contain just a single Id key, and it would hold two values. The values will always be in the same order they were in in the URL.

Setting the 404 and 405 responses

By default, trout will respond with http.StatusNotFound when no endpoint can fulfill the request, and http.StatusMethodNotAllowed when none of the endpoints that can fulfill the request are configured to respond to the HTTP method used. These defaults will also write a default error message as the response body. Furthermore, for http.StatusMethodNotAllowed responses, the Allow header will be set on the response, containing the methods the endpoint is configured to respond to.

Sometimes, however, you want something besides these defaults. In that case, you can set the Handle404 property on your router to the http.Handler you want to use for requests where the endpoint can’t be found, and Handle405 to the http.Handler you want to use for requests where an endpoint is matched, but isn’t configured to respond to the HTTP method used.

Getting extra information

trout sets two extra request headers when routing:

Understanding routing

There are times when multiple endpoints can be used to serve the same request. A simplified example would be /version and /{id} both being endpoints on a router. When a request is made to /version, which should be used?

Trout resolves this by trying to find the most specific route that can handle the request. Endpoints get a score based on how many placeholders exist in the endpoint, and how close to the beginning of the endpoint they are. The more placeholders an endpoint has, and the earlier in the endpoint they are, the lower the score is. The highest scoring endpoint serves the request.