package api import ( "encoding/json" "fmt" "io" "log" "net/http" "github.com/julienschmidt/httprouter" ) type DispatchFunction func(httprouter.Params, []byte) (interface{}, error) func apiHandler(f DispatchFunction) func(http.ResponseWriter, *http.Request, httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if addr := r.Header.Get("X-Forwarded-For"); addr != "" { r.RemoteAddr = addr } log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent()) // Read the body if r.ContentLength < 0 || r.ContentLength > 6553600 { http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}"), http.StatusRequestEntityTooLarge) return } var body []byte if r.ContentLength > 0 { tmp := make([]byte, 1024) for { n, err := r.Body.Read(tmp) for j := 0; j < n; j++ { body = append(body, tmp[j]) } if err != nil || n <= 0 { break } } } var ret interface{} var err error = nil ret, err = f(ps, body) // Format response resStatus := http.StatusOK if err != nil { ret = map[string]string{"errmsg": err.Error()} resStatus = http.StatusBadRequest log.Println(r.RemoteAddr, resStatus, err.Error()) } if ret == nil { ret = map[string]string{"errmsg": "Page not found"} resStatus = http.StatusNotFound } if str, found := ret.(string); found { w.Header().Set("Content-Type", "application/json") w.WriteHeader(resStatus) io.WriteString(w, str) } else if bts, found := ret.([]byte); found { w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", "attachment") w.Header().Set("Content-Transfer-Encoding", "binary") w.WriteHeader(resStatus) w.Write(bts) } else if j, err := json.Marshal(ret); err != nil { w.Header().Set("Content-Type", "application/json") http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err), http.StatusInternalServerError) } else { w.Header().Set("Content-Type", "application/json") w.WriteHeader(resStatus) w.Write(j) } } } func notFound(ps httprouter.Params, _ []byte) (interface{}, error) { return nil, nil }