{"action":"created","issue":{"url":"https://api.github.com/repos/gorilla/mux/issues/115","labels_url":"https://api.github.com/repos/gorilla/mux/issues/115/labels{/name}","comments_url":"https://api.github.com/repos/gorilla/mux/issues/115/comments","events_url":"https://api.github.com/repos/gorilla/mux/issues/115/events","html_url":"https://github.com/gorilla/mux/issues/115","id":98610482,"number":115,"title":"Subrouter consumes request.Body's read?","user":{"login":"Allendar","id":5161272,"avatar_url":"https://avatars.githubusercontent.com/u/5161272?v=3","gravatar_id":"","url":"https://api.github.com/users/Allendar","html_url":"https://github.com/Allendar","followers_url":"https://api.github.com/users/Allendar/followers","following_url":"https://api.github.com/users/Allendar/following{/other_user}","gists_url":"https://api.github.com/users/Allendar/gists{/gist_id}","starred_url":"https://api.github.com/users/Allendar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Allendar/subscriptions","organizations_url":"https://api.github.com/users/Allendar/orgs","repos_url":"https://api.github.com/users/Allendar/repos","events_url":"https://api.github.com/users/Allendar/events{/privacy}","received_events_url":"https://api.github.com/users/Allendar/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/gorilla/mux/labels/question","name":"question","color":"cc317c"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":9,"created_at":"2015-08-02T13:58:43Z","updated_at":"2015-08-03T18:05:04Z","closed_at":null,"body":"I was bumping my head against this issue for a while now. I have this subrouter and a call:\r\n\r\n```go\r\napiRoute := mainRoute.PathPrefix(\"/api\").Subrouter()\r\napiCatalogRoute := apiRoute.PathPrefix(\"/catalog\").Subrouter()\r\napiCatalogRoute.Handle(\"/\", handler(getCatalog))\r\n\r\nfunc getCatalog(w http.ResponseWriter, r *http.Request) (interface{}, *handleHTTPError) {\r\n\t// Decode data\r\n\tdecoder := json.NewDecoder(r.Body)\r\n\tvar Data struct {\r\n\t\tFilters          map[string]map[string]interface{}\r\n\t\tLanguages        map[string]string\r\n\t\tOrderBy          map[string]map[string]string\r\n\t\tLimit            int\r\n\t\tPage             int\r\n\t\tSearch           string\r\n\t\tSearchProperties map[string]map[string]string\r\n\t}\r\n\terr := decoder.Decode(&Data)\r\n\tlog.Println(err)\r\n\tif nil != err {\r\n\t\treturn nil, &handleHTTPError{err, \"Couldn't decode data\", http.StatusBadRequest}\r\n\t}\r\n\r\n\treturn map[string]interface{}{}, nil\r\n}\r\n```\r\n\r\nThis gives an EOF Error, indicating the r.Body.Read() has been consumed already..\r\n\r\nNow if I just do this:\r\n\r\n```go\r\nmainRoute.Handle(\"/api/catalog\", handler(getCatalog))\r\n```\r\n\r\n..it works..\r\n\r\nThis clearly indicates that the Subrouter() function consumes it."},"comment":{"url":"https://api.github.com/repos/gorilla/mux/issues/comments/127354495","html_url":"https://github.com/gorilla/mux/issues/115#issuecomment-127354495","issue_url":"https://api.github.com/repos/gorilla/mux/issues/115","id":127354495,"user":{"login":"Allendar","id":5161272,"avatar_url":"https://avatars.githubusercontent.com/u/5161272?v=3","gravatar_id":"","url":"https://api.github.com/users/Allendar","html_url":"https://github.com/Allendar","followers_url":"https://api.github.com/users/Allendar/followers","following_url":"https://api.github.com/users/Allendar/following{/other_user}","gists_url":"https://api.github.com/users/Allendar/gists{/gist_id}","starred_url":"https://api.github.com/users/Allendar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Allendar/subscriptions","organizations_url":"https://api.github.com/users/Allendar/orgs","repos_url":"https://api.github.com/users/Allendar/repos","events_url":"https://api.github.com/users/Allendar/events{/privacy}","received_events_url":"https://api.github.com/users/Allendar/received_events","type":"User","site_admin":false},"created_at":"2015-08-03T18:05:04Z","updated_at":"2015-08-03T18:05:04Z","body":"My apologies. Again:\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"encoding/json\"\r\n\t\"fmt\"\r\n\t\"github.com/gorilla/mux\"\r\n\t\"github.com/pquerna/ffjson/ffjson\"\r\n\t\"log\"\r\n\t\"net/http\"\r\n\t\"reflect\"\r\n)\r\n\r\nvar clFlagServerHost string\r\nvar clFlagServerPort int\r\n\r\nfunc main() {\r\n\tclFlagServerHost = \"127.0.0.1\"\r\n\tclFlagServerPort = 8888\r\n\r\n\tinitializeServer()\r\n}\r\n\r\n// Error response contains everything needed for http.Error\r\ntype handleHTTPError struct {\r\n\tError   error\r\n\tMessage string\r\n\tCode    int\r\n}\r\n\r\n// A custom type that we can use for handling errors and formatting responses\r\ntype handler func(w http.ResponseWriter, r *http.Request) (interface{}, *handleHTTPError)\r\n\r\n// Initialize all the server requirements and boot it\r\nfunc initializeServer() {\r\n\tinitializeRoutes()\r\n\r\n\t// go runHTTPSServer()\r\n\trunHTTPServer()\r\n}\r\n\r\n// Start the HTTP Server\r\nfunc runHTTPServer() {\r\n\tlog.Println(\"Starting HTTP Server..\")\r\n\taddr := fmt.Sprintf(\"%s:%d\", clFlagServerHost, clFlagServerPort)\r\n\terr := http.ListenAndServe(addr, nil) //http.HandlerFunc(redirectToHTTPS))\r\n\tif nil != err {\r\n\t\tpanic(err)\r\n\t}\r\n}\r\n\r\n// Initialize all routes and the static file serving handler\r\nfunc initializeRoutes() {\r\n\t// Setup routes\r\n\tmainRoute := mux.NewRouter()\r\n\tmainRoute.StrictSlash(true)\r\n\t// mainRoute.Handle(\"/\", http.RedirectHandler(\"/static/\", 302))\r\n\t// mainRoute.PathPrefix(\"/\").Handler(http.StripPrefix(\"/\", fileHandler))\r\n\r\n\tmainRoute.Handle(\"/api/catalog\", handler(getCatalog)) // FIXME :: Not as Subrouter() because it destroys r.Body..\r\n\r\n\t// apiCatalogRoute := apiRoute.PathPrefix(\"/catalog\").Subrouter()\r\n\t// apiCatalogRoute.Handle(\"/\", handler(getCatalog))\r\n\r\n\t// Bind generic route\r\n\thttp.Handle(\"/\", mainRoute)\r\n}\r\n\r\n// Attach the standard ServeHTTP method to our handler so the http library can call it\r\nfunc (function handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\r\n\t// start := time.Now().UnixNano()\r\n\t// log.Println(\"Request Recieved\", start)\r\n\tvar err error\r\n\t// Room for preparation code\r\n\t// ...\r\n\r\n\t// Call the actual handler\r\n\tresponse, errh := function(w, r)\r\n\r\n\t// Check for errors\r\n\tif nil != errh {\r\n\t\tif errh.Error != nil {\r\n\t\t\tlog.Printf(\"ERROR: %v\\n\", errh.Error)\r\n\t\t}\r\n\t\thttp.Error(w, fmt.Sprintf(`{\"error\":\"%s\"}`, errh.Message), errh.Code)\r\n\t\treturn\r\n\t}\r\n\r\n\tif response == nil {\r\n\t\tlog.Println(\"ERROR: response from method is nil\")\r\n\t\thttp.Error(w, \"Internal server error. Check the logs.\", http.StatusInternalServerError)\r\n\t\treturn\r\n\t}\r\n\r\n\tvar bytes []byte\r\n\t// Trigger buffer, otherwise turn the response into JSON\r\n\tif reflect.ValueOf(response).Kind() == reflect.Slice {\r\n\t\tbytes = response.([]byte)\r\n\t} else {\r\n\t\tbytes, err = ffjson.Marshal(response)\r\n\t\tif nil != err {\r\n\t\t\thttp.Error(w, \"Error marshaling JSON\", http.StatusInternalServerError)\r\n\t\t\treturn\r\n\t\t}\r\n\t}\r\n\r\n\t// Send the response and log\r\n\tw.Header().Set(\"Content-Type\", \"application/json\")\r\n\tw.Write(bytes)\r\n\t// end := time.Now().UnixNano()\r\n\t// fmt.Println(float64((end - start)) / float64(1e9))\r\n}\r\n\r\nfunc getCatalog(w http.ResponseWriter, r *http.Request) (interface{}, *handleHTTPError) {\r\n\t// Decode data\r\n\tdecoder := json.NewDecoder(r.Body)\r\n\tvar Data struct {\r\n\t\tFilters          map[string]map[string]interface{}\r\n\t\tLanguages        map[string]string\r\n\t\tOrderBy          map[string]map[string]string\r\n\t\tLimit            string\r\n\t\tPage             string\r\n\t\tSearch           string\r\n\t\tSearchProperties map[string]map[string]string\r\n\t}\r\n\terr := decoder.Decode(&Data)\r\n\tlog.Println(err)\r\n\tif nil != err {\r\n\t\treturn nil, &handleHTTPError{err, \"Couldn't decode data\", http.StatusBadRequest}\r\n\t}\r\n\r\n\tresponseData := map[string]map[string]interface{}{\r\n\t\t\"properties\": map[string]interface{}{},\r\n\t\t\"results\":    map[string]interface{}{},\r\n\t}\r\n\r\n\treturn responseData, nil\r\n}\r\n\r\n```"}}