-
Notifications
You must be signed in to change notification settings - Fork 13
/
doc.go
165 lines (126 loc) · 6.93 KB
/
doc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
Package Rye is a simple library to support http services.
Rye provides a middleware handler which can be used to chain http handlers together while providing
simple statsd metrics for use with a monitoring solution such as DataDog or other logging aggregators.
Rye also provides some additional middleware handlers that are entirely optional but easily consumed using Rye.
Setup
In order to use rye, you should vendor it and the statsd client within your project.
govendor fetch github.com/cactus/go-statsd-client/statsd
# Rye is a private repo, so we should clone it first
mkdir -p $GOPATH/github.com/InVisionApp
cd $GOPATH/github.com/InVisionApp
git clone git@github.com:InVisionApp/rye.git
govendor add github.com/InVisionApp/rye
Writing custom middleware handlers
Begin by importing the required libraries:
import (
"github.com/cactus/go-statsd-client/statsd"
"github.com/InVisionApp/rye"
)
Create a statsd client (if desired) and create a rye Config in order to pass in optional dependencies:
config := &rye.Config{
Statter: statsdClient,
StatRate: DEFAULT_STATSD_RATE,
}
Create a middleware handler. The purpose of the Handler is to keep Config and to provide an interface for chaining http handlers.
middlewareHandler := rye.NewMWHandler(config)
Build your http handlers using the Handler type from the **rye** package.
type Handler func(w http.ResponseWriter, r *http.Request) *rye.Response
Here are some example (custom) handlers:
func homeHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
fmt.Fprint(rw, "Refer to README.md for auth-api API usage")
return nil
}
func middlewareFirstHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
fmt.Fprint(rw, "This handler fires first.")
return nil
}
func errorHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
return &rye.Response {
StatusCode: http.StatusInternalServerError,
Err: errors.New(message),
}
}
Finally, to setup your handlers in your API
routes := mux.NewRouter().StrictSlash(true)
routes.Handle("/", middlewareHandler.Handle(
[]rye.Handler{
a.middlewareFirstHandler,
a.homeHandler,
})).Methods("GET")
log.Infof("API server listening on %v", ListenAddress)
srv := &http.Server{
Addr: ListenAddress,
Handler: routes,
}
srv.ListenAndServe()
Statsd Generated by Rye
Rye comes with built-in configurable `statsd` statistics that you could record to your favorite monitoring system. To configure that, you'll need to set up a `Statter` based on the `github.com/cactus/go-statsd-client` and set it in your instantiation of `MWHandler` through the `rye.Config`.
When a middleware is called, it's timing is recorded and a counter is recorded associated directly with the http status code returned during the call. Additionally, an `errors` counter is also sent to the statter which allows you to count any errors that occur with a code equaling or above 500.
Example: If you have a middleware handler you've created with a method named `loginHandler`, successful calls to that will be recorded to `handlers.loginHandler.2xx`. Additionally you'll receive stats such as `handlers.loginHandler.400` or `handlers.loginHandler.500`. You also will receive an increase in the `errors` count.
If you're sending your logs into a system such as DataDog, be aware that your stats from Rye can have prefixes such as `statsd.my-service.my-k8s-cluster.handlers.loginHandler.2xx` or even `statsd.my-service.my-k8s-cluster.errors`. Just keep in mind your stats could end up in the destination sink system with prefixes.
Using With Golang Context
With Golang 1.7, a new feature has been added that supports a request specific context.
This is a great feature that Rye supports out-of-the-box. The tricky part of this is how the context
is modified on the request. In Golang, the Context is always available on a
Request through `http.Request.Context()`. Great! However, if you want to add key/value pairs to the
context, you will have to add the context to the request before it gets passed to the next Middleware.
To support this, the `rye.Response` has a property called `Context`. This property takes a properly
created context (pulled from the `request.Context()` function. When you return a `rye.Response`
which has `Context`, the **rye** library will craft a new Request and make sure that the next middleware
receives that request.
Here's the details of creating a middleware with a proper `Context`. You must first pull from the
current request `Context`. In the example below, you see `ctx := r.Context()`. That pulls the current
context. Then, you create a NEW context with your additional context key/value. Finally, you
return `&rye.Response{Context:ctx}`
func addContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {
// Retrieve the request's context
ctx := r.Context()
// Create a NEW context
ctx = context.WithValue(ctx,"CONTEXT_KEY","my context value")
// Return that in the Rye response
// Rye will add it to the Request to
// pass to the next middleware
return &rye.Response{Context:ctx}
}
Now in a later middleware, you can easily retrieve the value you set!
func getContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {
// Retrieving the value is easy!
myVal := r.Context().Value("CONTEXT_KEY")
// Log it to the server log?
log.Infof("Context Value: %v", myVal)
return nil
}
For another simple example, look in the JWT middleware - it adds the JWT into the
context for use by other middlewares. It uses the `CONTEXT_JWT` key to push the
JWT token into the `Context`.
Using built-in middleware handlers
Rye comes with various pre-built middleware handlers. Pre-built middlewares source (and docs) can be found in the package dir following the pattern `middleware_*.go`.
To use them, specify the constructor of the middleware as one of the middleware handlers when you define your routes:
// example
routes.Handle("/", middlewareHandler.Handle(
[]rye.Handler{
rye.MiddlewareCORS(), // to use the CORS middleware (with defaults)
a.homeHandler,
})).Methods("GET")
OR
routes.Handle("/", middlewareHandler.Handle(
[]rye.Handler{
rye.NewMiddlewareCORS("*", "GET, POST", "X-Access-Token"), // to use specific config when instantiating the middleware handler
a.homeHandler,
})).Methods("GET")
A Note on the JWT Middleware
The JWT Middleware pushes the JWT token onto the Context for use by other middlewares in the chain.
This is a convenience that allows any part of your middleware chain quick access to the JWT.
Example usage might include a middleware that needs access to your user id or email address stored in the JWT.
To access this `Context` variable, the code is very simple:
func getJWTfromContext(rw http.ResponseWriter, r *http.Request) *rye.Response {
// Retrieving the value is easy!
// Just reference the rye.CONTEXT_JWT const as a key
myVal := r.Context().Value(rye.CONTEXT_JWT)
// Log it to the server log?
log.Infof("Context Value: %v", myVal)
return nil
}
*/
package rye