In this bit of code:
handler := func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "<html><body>Hello World!</body></html>")
}
What is the func keyword doing? I've been reading through the Tour of Go and I'm confused as to what is going on here.
EDITED: Added import list and function that it was apart of
It's part of a function here:
func ExampleResponseRecorder() {
handler := func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "<html><body>Hello World!</body></html>")
}
req := httptest.NewRequest("GET", "http://example.com/foo", nil)
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(resp.StatusCode)
fmt.Println(resp.Header.Get("Content-Type"))
fmt.Println(string(body))
// Output:
// 200
// text/html; charset=utf-8
// <html><body>Hello World!</body></html>
}
import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
)
func defines a closure, an annonymous function. handler is a variable that holds a reference to this function, which you can later call by passing the arguments in parenthesis:
handler(w, req)
Related
The whole question is in the title.
I was searching on SO if a subrouter will use a middleware of its parent, in the case the middleware is applied to the parent router with the method Use(), but I couldn't find a clear concise answer.
I couldn't find that information in the package documentation either, so I decided to test it and post a question and an answer here for everyone in the same case.
In the following code sample, does requesting on /john will trigger the logMiddleware ?
mainRouter := mux.NewRouter()
mainRouter.Use(logMiddleware)
subRouter := mainRouter.PathPrefix("/users/").Subrouter()
subRouter.Handle("/john", johnHandler())
Yes, mux subrouters inherit their parent's middlewares when applied with Use() method.
Here is the test I created in case you want to try in your favorite IDE :
router code
package so
import (
"context"
"net/http"
"github.com/gorilla/mux"
)
func newRouter(useMainMiddleware bool) mux.Router {
mainRouter := mux.NewRouter()
if useMainMiddleware {
mainRouter.Use(middleware)
}
subRouter := mainRouter.PathPrefix("/users/").Subrouter()
subRouter.Handle("/test", testHandler())
return *mainRouter
}
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(context.WithValue(r.Context(), "is_using_middleware", true))
next.ServeHTTP(w, r)
})
}
func testHandler() http.Handler {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if using, castOk := r.Context().Value("is_using_middleware").(bool); castOk && using {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusInternalServerError)
return
},
)
}
test file
package so
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSubrouterWithMiddleware(t *testing.T) {
// GIVEN
request := httptest.NewRequest(http.MethodGet, "/users/test", nil)
recorder := httptest.NewRecorder()
router := newRouter(true) // using a middleware
// WHEN
router.ServeHTTP(recorder, request)
// THEN
assert.Equal(t, http.StatusOK, recorder.Result().StatusCode)
}
func TestSubrouterWithoutMiddleware(t *testing.T) {
// GIVEN
request := httptest.NewRequest(http.MethodGet, "/users/test", nil)
recorder := httptest.NewRecorder()
router := newRouter(false) // not using a middleware
// WHEN
router.ServeHTTP(recorder, request)
// THEN
assert.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode)
}
In the code below, I am serving a PostForm request, and when I run the code I am getting an output of Age=20&Name=Mike, instead of a map like {"Name":Mike,"Age":"20"}. Is that output appropriate or am I missing something?
Also what is the difference between a PostForm request and a Post request?
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
func server() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
content, _ := ioutil.ReadAll(r.Body)
fmt.Println(string(content))
})
log.Fatal(http.ListenAndServe("", nil))
}
func client() {
data := url.Values{}
data.Add("Name", "Mike")
data.Add("Age", "20")
response, err := http.PostForm("http://localhost/", data)
if err != nil {
return
}
defer response.Body.Close()
}
func main() {
go server()
client()
}
For example, I want to do /api/v1/users/id/{id}.
At the moment, I have this:
mux := http.NewServeMux()
mux.Handle("/api/v1/users", HandleUsersV1{db: db, mux: mux})
log.Fatal(http.ListenAndServe(fmt.Sprintf("%s%d", ":", portNumber), mux))
I want:
mux := http.NewServeMux()
mux.Handle("/api/v1", HandleV1{})
And then in HandleV1:
mux.HandleFunc("/users/{id}", handler)
I know Gorilla Mux can do it for me with PathPrefix, but I prefer net/http.
The standard net/http does not support dynamic path segments, so /{id} is not gonna work the way you might imagine. As for the prefix thing, you can use this https://golang.org/pkg/net/http/#StripPrefix.
v1mux := http.NewServeMux()
v1mux.HandleFunc("/users/", handler)
mux := http.NewServeMux()
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", v1mux))
log.Fatal(http.ListenAndServe(fmt.Sprintf("%s%d", ":", portNumber), mux))
I suggest using https://github.com/julienschmidt/httprouter. But if you insist.
package main
import (
"log"
"net/http"
"strconv"
"strings"
)
func main() {
res := resourceone{}
http.HandleFunc("/api/v1/users/", res.router)
log.Fatal(http.ListenAndServe(":8080", nil))
}
type resourceone struct {
}
func (res *resourceone) router(w http.ResponseWriter, r *http.Request) {
args := strings.Split(r.URL.String()[len(`/api/v1/users/`):], `/`)
switch args[0] {
case `id`:
id, _ := strconv.Atoi(args[1])
res.one(w, r, id)
case `name`:
res.two(w, r, args[1])
}
}
func (res *resourceone) one(w http.ResponseWriter, r *http.Request, id int) {
w.Write([]byte(strconv.Itoa(id)))
}
func (res *resourceone) two(w http.ResponseWriter, r *http.Request, name string) {
w.Write([]byte(name))
}
Test:
curl 127.0.0.1:8080/api/v1/users/name/david
curl 127.0.0.1:8080/api/v1/users/id/1234
I'm writing test code for martini app working as a reverse proxy in go, and want to test it using httptest.ResponseRecorder, but I got an error the following.
[martini] PANIC: interface conversion: *httptest.ResponseRecorder is not http.CloseNotifier: missing method CloseNotify
httptest.ResponseRecorder has no method CloseNotify()
How should I test it?
package main
import (
"github.com/go-martini/martini"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"testing"
)
func TestReverseProxy(t *testing.T) {
// Mock backend
backendResponse := "I am the backend"
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(backendResponse))
}))
defer backend.Close()
backendURL, _ := url.Parse(backend.URL)
// Frontend
m := martini.Classic()
m.Get("/", func(w http.ResponseWriter, r *http.Request) {
proxy := httputil.NewSingleHostReverseProxy(backendURL)
proxy.ServeHTTP(w, r)
})
// Testing
req, _ := http.NewRequest("GET", "/", nil)
res := httptest.NewRecorder()
m.ServeHTTP(res, req)
assert.Equal(t, 200, res.Code, "should be equal")
}
First, please note that the martini framework is no longer maintained as said in their README.
Then, about your issue, it's because Martini does something that looks pretty bad to me: it takes an http.ResponseWriter and assumes it is also an http.CloseNotifier, while there is absolutely no guarantee of this. They should take a custom interface wrapping both of them, something like that:
type ResponseWriterCloseNotifier interface {
http.ResponseWriter
http.CloseNotifier
}
You can see in their source code that they had the same issue for their own tests, and used some workaround: https://github.com/go-martini/martini/commit/063dfcd8b0f64f4e2c97f0bc27fa422969baa23b#L13
Here is some working code made with it:
package main
import (
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"testing"
"github.com/go-martini/martini"
"github.com/stretchr/testify/assert"
)
type closeNotifyingRecorder struct {
*httptest.ResponseRecorder
closed chan bool
}
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
return &closeNotifyingRecorder{
httptest.NewRecorder(),
make(chan bool, 1),
}
}
func (c *closeNotifyingRecorder) close() {
c.closed <- true
}
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
return c.closed
}
func TestReverseProxy(t *testing.T) {
// Mock backend
backendResponse := "I am the backend"
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(backendResponse))
}))
defer backend.Close()
backendURL, _ := url.Parse(backend.URL)
// Frontend
m := martini.Classic()
m.Get("/", func(w http.ResponseWriter, r *http.Request) {
proxy := httputil.NewSingleHostReverseProxy(backendURL)
proxy.ServeHTTP(w, r)
})
// Testing
req, _ := http.NewRequest("GET", "/", nil)
res := newCloseNotifyingRecorder()
m.ServeHTTP(res, req)
assert.Equal(t, 200, res.Code, "should be equal")
}
How do I properly refer to route names from inside handlers?
Should mux.NewRouter() be assigned globally instead of standing inside a function?
func AnotherHandler(writer http.ResponseWriter, req *http.Request) {
url, _ := r.Get("home") // I suppose this 'r' should refer to the router
http.Redirect(writer, req, url, 302)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", HomeHandler).Name("home")
r.HandleFunc("/nothome/", AnotherHandler).Name("another")
http.Handle("/", r)
http.ListenAndServe(":8000", nil)
}
You have the method mux.CurrentRoute() that returns the route for a given request. From that request, you can create a subrouter and call Get("home")
Example: (play: http://play.golang.org/p/Lz10YUyP6e)
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func HomeHandler(writer http.ResponseWriter, req *http.Request) {
writer.WriteHeader(200)
fmt.Fprintf(writer, "Home!!!\n")
}
func AnotherHandler(writer http.ResponseWriter, req *http.Request) {
url, err := mux.CurrentRoute(req).Subrouter().Get("home").URL()
if err != nil {
panic(err)
}
http.Redirect(writer, req, url.String(), 302)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/home", HomeHandler).Name("home")
r.HandleFunc("/nothome/", AnotherHandler).Name("another")
http.Handle("/", r)
http.ListenAndServe(":8000", nil)
}