diff --git a/transport/http/codec.go b/transport/http/codec.go index 79db57478..176e599af 100644 --- a/transport/http/codec.go +++ b/transport/http/codec.go @@ -12,6 +12,12 @@ import ( // SupportPackageIsVersion1 These constants should not be referenced from any other code. const SupportPackageIsVersion1 = true +// Redirector replies to the request with a redirect to url +// which may be a path relative to the request path. +type Redirector interface { + Redirect() (string, int) +} + // Request type net/http. type Request = http.Request @@ -49,8 +55,12 @@ func DefaultRequestDecoder(r *http.Request, v interface{}) error { // DefaultResponseEncoder encodes the object to the HTTP response. func DefaultResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{}) error { if v == nil { - _, err := w.Write(nil) - return err + return nil + } + if rd, ok := v.(Redirector); ok { + url, code := rd.Redirect() + http.Redirect(w, r, url, code) + return nil } codec, _ := CodecForRequest(r, "Accept") data, err := codec.Marshal(v) diff --git a/transport/http/redirect.go b/transport/http/redirect.go new file mode 100644 index 000000000..cb98ac09b --- /dev/null +++ b/transport/http/redirect.go @@ -0,0 +1,18 @@ +package http + +type redirect struct { + URL string + Code int +} + +func (r *redirect) Redirect() (string, int) { + return r.URL, r.Code +} + +// NewRedirect new a redirect with url, which may be a path relative to the request path. +// The provided code should be in the 3xx range and is usually StatusMovedPermanently, StatusFound or StatusSeeOther. +// If the Content-Type header has not been set, Redirect sets it to "text/html; charset=utf-8" and writes a small HTML body. +// Setting the Content-Type header to any value, including nil, disables that behavior. +func NewRedirect(url string, code int) Redirector { + return &redirect{URL: url, Code: code} +} diff --git a/transport/http/redirect_test.go b/transport/http/redirect_test.go new file mode 100644 index 000000000..b98bf4f0b --- /dev/null +++ b/transport/http/redirect_test.go @@ -0,0 +1,23 @@ +package http + +import ( + "net/http/httptest" + "testing" +) + +func TestRedirect(t *testing.T) { + var ( + redirectURL = "/redirect" + redirectCode = 302 + ) + r := httptest.NewRequest("POST", "/test", nil) + w := httptest.NewRecorder() + _ = DefaultResponseEncoder(w, r, NewRedirect(redirectURL, redirectCode)) + + if w.Code != redirectCode { + t.Fatalf("want %d but got %d", redirectCode, w.Code) + } + if v := w.Header().Get("Location"); v != redirectURL { + t.Fatalf("want %s but got %s", redirectURL, v) + } +}