-
-
Save justinas/7059324 to your computer and use it in GitHub Desktop.
| package main | |
| import ( | |
| "net/http" | |
| ) | |
| type SingleHost struct { | |
| handler http.Handler | |
| allowedHost string | |
| } | |
| func NewSingleHost(handler http.Handler, allowedHost string) *SingleHost { | |
| return &SingleHost{handler: handler, allowedHost: allowedHost} | |
| } | |
| func (s *SingleHost) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| host := r.Host | |
| if host == s.allowedHost { | |
| s.handler.ServeHTTP(w, r) | |
| } else { | |
| w.WriteHeader(403) | |
| } | |
| } | |
| func myHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Write([]byte("Success!")) | |
| } | |
| func main() { | |
| single := NewSingleHost(http.HandlerFunc(myHandler), "example.com") | |
| println("Listening on port 8080") | |
| http.ListenAndServe(":8080", single) | |
| } |
| package main | |
| import ( | |
| "net/http" | |
| ) | |
| func SingleHost(handler http.Handler, allowedHost string) http.Handler { | |
| ourFunc := func(w http.ResponseWriter, r *http.Request) { | |
| host := r.Host | |
| if host == allowedHost { | |
| handler.ServeHTTP(w, r) | |
| } else { | |
| w.WriteHeader(403) | |
| } | |
| } | |
| return http.HandlerFunc(ourFunc) | |
| } | |
| func myHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Write([]byte("Success!")) | |
| } | |
| func main() { | |
| single := SingleHost(http.HandlerFunc(myHandler), "example.com") | |
| println("Listening on port 8080") | |
| http.ListenAndServe(":8080", single) | |
| } |
| package main | |
| import ( | |
| "net/http" | |
| ) | |
| type AppendMiddleware struct { | |
| handler http.Handler | |
| } | |
| func (a *AppendMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| a.handler.ServeHTTP(w, r) | |
| w.Write([]byte("<!-- Middleware says hello! -->")) | |
| } | |
| func myHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Write([]byte("Success!")) | |
| } | |
| func main() { | |
| mid := &AppendMiddleware{http.HandlerFunc(myHandler)} | |
| println("Listening on port 8080") | |
| http.ListenAndServe(":8080", mid) | |
| } |
| package main | |
| import ( | |
| "net/http" | |
| "net/http/httptest" | |
| ) | |
| type ModifierMiddleware struct { | |
| handler http.Handler | |
| } | |
| func (m *ModifierMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| rec := httptest.NewRecorder() | |
| // passing a ResponseRecorder instead of the original RW | |
| m.handler.ServeHTTP(rec, r) | |
| // after this finishes, we have the response recorded | |
| // and can modify it before copying it to the original RW | |
| // we copy the original headers first | |
| for k, v := range rec.Header() { | |
| w.Header()[k] = v | |
| } | |
| // and set an additional one | |
| w.Header().Set("X-We-Modified-This", "Yup") | |
| // only then the status code, as this call writes the headers as well | |
| w.WriteHeader(418) | |
| // The body hasn't been written (to the real RW) yet, | |
| // so we can prepend some data. | |
| data := []byte("Middleware says hello again. ") | |
| // But the Content-Length might have been set already, | |
| // we should modify it by adding the length | |
| // of our own data. | |
| // Ignoring the error is fine here: | |
| // if Content-Length is empty or otherwise invalid, | |
| // Atoi() will return zero, | |
| // which is just what we'd want in that case. | |
| clen, _ := strconv.Atoi(r.Header.Get("Content-Length")) | |
| clen += len(data) | |
| w.Header.Set("Content-Length", strconv.Itoa(clen)) | |
| // finally, write out our data | |
| w.Write(data) | |
| // then write out the original body | |
| w.Write(rec.Body.Bytes()) | |
| } | |
| func myHandler(w http.ResponseWriter, r *http.Request) { | |
| w.Write([]byte("Success!")) | |
| } | |
| func main() { | |
| mid := &ModifierMiddleware{http.HandlerFunc(myHandler)} | |
| println("Listening on port 8080") | |
| http.ListenAndServe(":8080", mid) | |
| } |
Hi justinas !
Great job.
Noob question. Do you see any chance to make alice and https://github.com/julienschmidt/httprouter working together without rewriting code? Issue I see, httprouter has additional in handler.
Thank you in advance
There are two problems with 4_modifier.go.
- You should not ignore the error return values of the calls to
w.Write. Ignoring them makes it much harder to track down the issues caused by problem #2. - If the Handler that you are wrapping sets a Content-Length header and you write that header as-is to your new ResponseWriter and you make a change to the response's content that increases its length then Write will fail and return an error. If you do not check for that error (see #1 above) and just silently proceed you'll be left to scratch your head, wondering where you content went. See bradfitz's explanation in this thread in the golang-nuts forum.
@hartzell, thanks so much. I think @pengfei-xue referred to the same issue, though I had trouble understanding it.
I have updated the gist (and the blog post) for #2, though I am not sure about #1. It would make sense to add it everywhere, instead of just that one example, as weird stuff might happen anywhere and in theory, every Write() could fail.
What would be a good way for your modifier middleware to alter the HTTP Status Code of the response? I found some old discussion on the GoNuts list from 30 months ago but none of what they suggested seems to work anymore!
DOH! Nevermind. ResponseRecorder has it. I should have looked closer. Silly Me!
@justinas,
I think, that you should use w instead of r while updating Content-Length (and read it from rec). I'm right?
@Komosa, yes, thank you!
Very good resources! I just made one lecture based on your blog about middleware, but that's in Chinese in case you might be interested in https://github.com/Unknwon/go-web-foundation/blob/master/lectures/lecture10/lecture10.md