Documentation
¶
Overview ¶
Package middleware provides flexibility at the HTTP request/response process. For this purpose, the Adapter pattern is used, which consist in wrapping handlers. An adapter may run code before and/or after the handler it is wrapping.
func MyAdapter(h http.Handler) http.Handler { nh := func(w http.ResponseWriter, r *http.Request) { // Code that run before h.ServeHTTP(w, r) // Breaks the flow if not used // Code that run after } return http.HandlerFunc(nh) }
When multiple adapters are used, the result is a sequence of wrappers and its execution flow depends in the order that adapters were given.
Adapt(h, f1, f2, f3)
1. f1 before code
2. f2 before code
3. f3 before code
4. h
5. f3 after code
6. f2 after code
7. f1 after code
Some adapters my require to change the behavior of the ResponseWriter. Since the underlying type of a ResponseWriter from the stdlib implements more than this interface (which is a bad design decision), simply wrapping it with a custom type will hide other interface implementations. This leads to several side effects during request processing and makes some middleware completely unusable.
To solve this, AdaptResponseWriter must be used. Supported interfaces are:
* http.Flusher
* io.ReaderFrom
Unsupported interfaces are:
* http.CloseNotifier: this was deprecated in favor of Request.Context.
* http.Hijacker: I don't like Websockets (or the way they are implemented) and hijacking HTTP is a hacky workaround (also, ResponseWriters for HTTP versions higher than 1.1 don't implement this interface).
* http.Pusher: web browsers are deprecating HTTP/2 Server Push.
Index ¶
- func Adapt(h http.Handler, a ...Adapter) http.Handler
- func AdaptFunc(h func(w http.ResponseWriter, r *http.Request), a ...Adapter) http.Handler
- func AdaptResponseWriter(w http.ResponseWriter, m ResponseWriterMethods) http.ResponseWriter
- func IsAdaptedResponseWriter(w http.ResponseWriter) bool
- type Adapter
- func AddHeader(key, value string) Adapter
- func Cache(directives string) Adapter
- func DelHeader(key string) Adapter
- func Gzip(level int) Adapter
- func JSONRequest() Adapter
- func JSONResponse() Adapter
- func Replace(old, n string) Adapter
- func SetHeader(key, value string) Adapter
- func StripPrefix(prefix string) Adapter
- type ResponseWriteAdapter
- type ResponseWriterMethods
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Adapt ¶
Adapt wraps a http.Handler into a list of Adapters. Takes a http.Handler and a list of Adapters, which will be wrapped from right to left (and ran left to right).
Example ¶
package main import ( "fmt" "net/http" "net/http/httptest" "go.ntrrg.dev/ntgo/net/http/middleware" ) func main() { h := middleware.Adapt( http.FileServer(http.Dir(".")), middleware.Cache("max-age=3600, s-max-age=3600"), middleware.Gzip(-1), ) // http.Handle("/", h) w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/", nil) r.Header.Set("Accept-Encoding", "gzip") h.ServeHTTP(w, r) res := w.Result() defer res.Body.Close() fmt.Printf("Status: %v\n", res.Status) fmt.Printf("Cache-Control: %+v\n", res.Header.Get("Cache-Control")) fmt.Printf("Content-Encoding: %v", res.Header.Get("Content-Encoding")) }
Output: Status: 200 OK Cache-Control: max-age=3600, s-max-age=3600 Content-Encoding: gzip
func AdaptFunc ¶
AdaptFunc works as Adapt but for http.HandlerFunc.
Example ¶
package main import ( "fmt" "net/http" "net/http/httptest" "go.ntrrg.dev/ntgo/net/http/middleware" ) func main() { h := middleware.AdaptFunc( func(w http.ResponseWriter, r *http.Request) {}, middleware.JSONResponse(), middleware.Cache("max-age=3600, s-max-age=3600"), ) // http.Handle("/", h) w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/", nil) h.ServeHTTP(w, r) res := w.Result() defer res.Body.Close() fmt.Printf("Status: %v\n", res.Status) fmt.Printf("Cache-Control: %+v\n", res.Header.Get("Cache-Control")) fmt.Printf("Content-Type: %v", res.Header.Get("Content-Type")) }
Output: Status: 200 OK Cache-Control: max-age=3600, s-max-age=3600 Content-Type: application/json; charset=utf-8
func AdaptResponseWriter ¶ added in v0.7.0
func AdaptResponseWriter( w http.ResponseWriter, m ResponseWriterMethods, ) http.ResponseWriter
AdaptResponseWriter wraps a ResponseWriter with custom methods. This allows to overwrite only specific methods without compromising other interface implementations.
This technique is based on https://github.com/felixge/httpsnoop.
func IsAdaptedResponseWriter ¶ added in v0.7.0
func IsAdaptedResponseWriter(w http.ResponseWriter) bool
IsAdaptedResponseWriter reports if the given http.ResponseWriter were adapted previously.
Types ¶
type Adapter ¶
Adapter is a wrapper for http.Handler. Takes a http.Handler as argument and creates a new one that may run code before and/or after calling the given handler.
func Cache ¶
Cache sets HTTP cache headers for GET requests.
Supported directives:
* public/private: whether the cached response is for any or a specific user.
* max-age=TIME: cache life time in seconds. The maximum value is 1 year.
* s-max-age=TIME: same as max-age, but this one has effect in proxies.
* must-revalidate: force expired cached response revalidation, even in special circumstances (like slow connections, were cached responses are used even after they had expired).
* proxy-revalidate: same as must-revalidate, but this one has effect in proxies.
* no-cache: disables cache.
* no-store: disables cache, even in proxies.
func Gzip ¶
Gzip compresses the response body. The compression level is given as an integer value according to the compress/flate package.
func JSONRequest ¶
func JSONRequest() Adapter
JSONRequest checks that request has the appropriate HTTP method and the appropriate 'Content-Type' header. Responds with http.StatusMethodNotAllowed if the used method is not one of POST, PUT or PATCH. Responds with http.StatusUnsupportedMediaType if the 'Content-Type' header is not valid.
func JSONResponse ¶
func JSONResponse() Adapter
JSONResponse prepares the response to be a JSON response.
func StripPrefix ¶
StripPrefix strips the given prefix from the request URL.
type ResponseWriteAdapter ¶ added in v0.7.0
type ResponseWriteAdapter interface { // Unwrap allows access to the underlying http.ResponseWriter. Unwrap() http.ResponseWriter }
A ResponseWriteAdapter represents an adapted http.ResponseWriter.
type ResponseWriterMethods ¶ added in v0.7.0
type ResponseWriterMethods struct { // http.ResponseWriter Header func() http.Header Write func([]byte) (int, error) WriteHeader func(int) // http.Flusher Flush func() // io.ReaderFrom ReadFrom func(io.Reader) (int64, error) }
ResponseWriterMethods is a set of methods used to wrap a http.ReponseWriter.