// We can override Content-Type func TestEnableToOverrideDefaultContentType(t *testing.T) { request, _ := http.NewRequest("GET", "/", nil) response := httptest.NewRecorder() a, err := dou.NewAPI("jsonapi") a.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") }) if err != nil { panic(err) } a.LogStackTrace = false if err != nil { panic(err) } a.ServeHTTP(response, request) if response.Header().Get("Content-Type") != "text/plain" { t.Error("default content type that is set by jsonapi.DefaultBeforeDispatch should be overridable") } }
// APIStatus should set X-API-Status header func TestAPIStatus(t *testing.T) { request, _ := http.NewRequest("GET", "/", nil) response := httptest.NewRecorder() a, err := dou.NewAPI("jsonapi") a.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { a.APIStatus(w, 999) }) if err != nil { panic(err) } a.LogStackTrace = false if err != nil { panic(err) } a.ServeHTTP(response, request) if response.Header().Get("X-API-Status") != "999" { t.Errorf("APIStatus should set X-API-Status. (expect) = \"999\", but (got) = %v", response.Header().Get("X-API-Status")) } }
// OnPanic should not write if response is already written before panic occur func TestOnPanicDontWriteIfResponseIsAlreadyWrittenBeforePanicOccur(t *testing.T) { request, _ := http.NewRequest("GET", "/", nil) response := httptest.NewRecorder() a, err := dou.NewAPI("jsonapi") a.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello")) panic("<test panic>") }) if err != nil { panic(err) } a.LogStackTrace = false if err != nil { panic(err) } a.ServeHTTP(response, request) if !bytes.Equal(response.Body.Bytes(), []byte("hello")) { t.Error("OnPanic should not write response if response is written before panic occur") } }
// OnPanic should write response if panic occur func TestOnPanicWriteErrorMessage(t *testing.T) { request, _ := http.NewRequest("GET", "/", nil) response := httptest.NewRecorder() a, err := dou.NewAPI("jsonapi") a.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("<test panic>") }) if err != nil { panic(err) } a.LogStackTrace = false if err != nil { panic(err) } a.ServeHTTP(response, request) if err != nil { panic(err) } gotContentType := response.Header().Get("Content-Type") switch { case strings.HasPrefix(gotContentType, "application/json;"): if string(response.Body.Bytes()) == "" { t.Error("OnPanic should write error message.") } else { gotJSON := map[string]string{} err := json.Unmarshal(response.Body.Bytes(), &gotJSON) if err != nil { panic(err) } if gotJSON["message"] != http.StatusText(http.StatusInternalServerError) { t.Errorf("OnPanic wrote invalid error message. (got) = %s", response.Body.Bytes()) } } case strings.HasPrefix(gotContentType, "text/plain;"): if string(response.Body.Bytes()) != http.StatusText(http.StatusInternalServerError) { t.Errorf("OnPanic wrote invalid error message. (expect) = %s, (got) = %s", http.StatusText(http.StatusInternalServerError), response.Body.Bytes()) } default: t.Errorf("Unexpected Content-Type. (expect has prefix) = text/plain or application/json, (got) = %v", gotContentType) } }
// BeforeDispatch should set Content-Type func TestSetDefaultContentType(t *testing.T) { request, _ := http.NewRequest("GET", "/", nil) response := httptest.NewRecorder() a, err := dou.NewAPI("jsonapi") a.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { }) if err != nil { panic(err) } a.LogStackTrace = false if err != nil { panic(err) } a.ServeHTTP(response, request) if response.Header().Get("Content-Type") != "application/json; charset=utf-8" { t.Errorf("jsonapi.DefaultBeforeDispatch should set default content type `application/json; charset=utf-8`, but got %v", response.Header().Get("Content-Type")) } }
func main() { defer teardown() // --- Setup Router --- // ! You can use router keeping interface `api.Router` instead of github.com/ToQoz/rome router := rome.NewRouter() router.NotFoundFunc(func(w http.ResponseWriter, r *http.Request) { j, err := json.Marshal(map[string]string{ "message": http.StatusText(http.StatusNotFound), "documentation_url": "http://toqoz.net", }) if err != nil { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } w.WriteHeader(http.StatusNotFound) // skip wrote bytesyze _, err = fmt.Fprintln(w, string(j)) if err != nil { log.Printf("dou: fail to fmt.Fpintln(http.ResponseWriter, string)\n%v", err) } }) // --- Setup API --- api, err := dou.NewAPI("jsonapi") api.Handler = router //api, err := dou.NewAPI("jsonapi") if err != nil { log.Fatal(err) } api.BeforeDispatch = func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) { // Call default w, r = api.Plugin.BeforeDispatch(w, r) lw := apachelog.NewLoggingWriter(w, r, logger) return lw, r } api.AfterDispatch = func(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) { // Call default w, r = api.Plugin.AfterDispatch(w, r) if lw, ok := w.(*apachelog.LoggingWriter); ok { lw.EmitLog() } return w, r } api.ReadTimeout = 10 * time.Second api.WriteTimeout = 10 * time.Second api.MaxHeaderBytes = 1 << 20 // --- Map routes --- router.GetFunc("/users", func(w http.ResponseWriter, r *http.Request) { api.APIStatus(w, APIStatusOk) api.Ok(w, users, http.StatusOK) }) router.GetFunc("/error", func(w http.ResponseWriter, r *http.Request) { api.APIStatus(w, APIStatusUnexpectedError) api.Error(w, map[string]string{"message": "Internal server error"}, http.StatusInternalServerError) }) // Try Ok $ curl -X POST -d 'name=ToQoz&[email protected]' -D - :8099/users // Try Error $ curl -X POST -D - :8099/users router.PostFunc("/users", func(w http.ResponseWriter, r *http.Request) { u := &User{ Name: r.FormValue("name"), Email: r.FormValue("email"), } errs := u.Validate() if len(errs) > 0 { api.APIStatus(w, APIStatusValidationError) api.Error(w, newAPIErrors(errs), 422) return } err := u.Save() if err != nil { api.APIStatus(w, APIStatusUnexpectedError) api.Error(w, newAPIErrors(errs), http.StatusInternalServerError) return } api.APIStatus(w, APIStatusOk) api.Ok(w, u, http.StatusCreated) }) // --- Create listener --- // You can use utility, for example github.com/lestrrat/go-server-starter-listener etc. l, err := net.Listen("tcp", ":8099") if err != nil { log.Printf("Could not listen: %s", ":8099") teardown() os.Exit(1) } log.Printf("Listen: %s", ":8099") // --- Handle C-c --- c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for sig := range c { log.Print("Stopping the server...") switch sig { case os.Interrupt: // --- Stop Server --- api.Stop() return default: log.Print("Receive unknown signal...") } } }() // --- Run Server --- api.Run(l) }
func main() { defer teardown() api, err := dou.NewAPI("jsonapi") if err != nil { log.Fatal(err) } api.ReadTimeout = 10 * time.Second api.WriteTimeout = 10 * time.Second api.MaxHeaderBytes = 1 << 20 // --- Map routes --- api.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/": api.Ok(w, map[string]string{"hello": "world"}, http.StatusOK) case "/error": err := errors.New("some error occur") api.Error(w, newAPIError(err), http.StatusInternalServerError) case "/errors": var errs []error errs = append(errs, errors.New("1 error occur")) errs = append(errs, errors.New("2 error occur")) api.Error(w, newAPIErrors(errs), http.StatusInternalServerError) default: api.Error(w, map[string]string{"message": http.StatusText(http.StatusNotFound)}, http.StatusNotFound) } }) // --- Create listener --- // You can use utility, for example github.com/lestrrat/go-server-starter-listener etc. l, err := net.Listen("tcp", ":8099") if err != nil { log.Printf("Could not listen: %s", ":8099") teardown() os.Exit(1) } log.Printf("Listen: %s", ":8099") // --- Handle C-c --- c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for sig := range c { log.Print("Stopping the server...") switch sig { case os.Interrupt: // --- Stop Server --- api.Stop() return default: log.Print("Receive unknown signal...") } } }() // --- Run Server --- api.Run(l) }