Updated Writing Middleware (markdown)

Matt Holt 2015-11-07 22:32:48 -07:00
parent e2acac2b95
commit 063c80d0c5

@ -18,39 +18,35 @@ type MyHandler struct {
}
```
It must also implement the middleware.Handler interface. We do this by adding a method called ServeHTTP and two one-liners called GetNext and SetNext. Right now, let's just pass the request to the next Handler in the chain:
It must also implement the middleware.Handler interface. We do this by adding a method called ServeHTTP and two one-liners called GetNext and SetNext. This example just passes the request to the next Handler in the chain:
```go
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
return h.Next.ServeHTTP(w, r)
}
// These allow the middleware stack to be traversed like a singly-linked list (the API uses this)
func (h *MyHandler) GetNext() middleware.Handler { return h.Next }
func (h *MyHandler) SetNext(next middleware.Handler) { h.Next = next }
```
That's all there is to it. Put your middleware logic in the ServeHTTP method, and add any properties that your middleware may need to access to the struct type you defined your middleware to be.
Be sure to use pointer receivers.
Be sure to use pointer receivers, and be careful of race conditions -- avoid state whenever possible.
## Return Values and Writing Responses
Oh yes, those pesky return values on `ServeHTTP()`. You read the documentation so you already know what they mean. But what does that imply for the behavior of your middleware?
Some handlers are endpoints: they create a response and write it along with the status code to the client. Effectively, this terminates the chain. You wouldn't want to write a document out to the client and then tell the next handler in the chain, "Okay it's all yours!" In this situation, you would *not* call Next().
Some handlers are endpoints: they create a response and write it along with the status code to the client. Effectively, this terminates the chain. You wouldn't want to write a document out to the client and then tell the next handler in the chain, "Your turn to respond!" In that situation, you would *not* call Next().
So you've written the response to the client and you aren't calling the next handler. What do you return? If your handler completed successfully (and wrote the response), simply return the status code you wrote and a nil error.
But what if there was an error? If your handler fails and the client should receive an error response (HTTP status >= 400), your handler *should NOT* write anything—not even the status code—to the response. To handle errors consistently, the application or a dedicated error-handling middleware should take care of that (Caddy already has such middleware).
Also note that the status code return value is for the client's benefit, and the error return value is for the server. The server should (probably) not write the error string to the client. Instead, it should log it or report it privately somehow. The client should, however, receive a response with the appropriate status code. An error status code doesn't always have to correspond to a non-nil error value (for example, a 404 error doesn't usually require an entry in the error log).
The status code return value is for the client's benefit, and the error return value is for the server's benefit. Since error values may be logged by the server, an error status doesn't always have to correspond with a non-nil error value (for example, a 404 error doesn't usually require an entry in the error log).
## Integrating with Caddy
Now that your general-purpose middleware handler is working, it's time to integrate it with Caddy. Caddy parses middleware configuration out of a configuration file, so Caddy provides a parse facility for you to easily set up your middleware based on what the user typed in the text file.
In your package, create a `Setup` function which will be used to set up your middleware. It must parse the Caddyfile and fill in values as needed. Caddy will use this when the server starts to chain it in, start any necessary services, and get everything running. For example:
In your package, create a `Setup` function which will be used to set up your middleware. It must parse the Caddyfile and fill in values as needed. Caddy will use this when the server starts to chain in the handler, start any necessary services, and get everything running. For example:
```go
// Setup configures a new MyHandler middleware instance.
@ -59,13 +55,13 @@ func Setup(c *setup.Controller) (middleware.Middleware, error) {
// do parsing
}
// Don't create your middleware struct in this function!
// Don't create your middleware struct in this function,
// do it in the return value below.
return func(next middleware.Handler) middleware.Handler {
// This function will be called every time the server
// is started. Create and return your middleware
// struct here to ensure it gets the latest config.
// Return a pointer - very important!
return &MyHandler{
Next: next,
// ...