diff --git a/docs/advanced-guide/overriding-default/page.md b/docs/advanced-guide/overriding-default/page.md index c7f515578..e54df6ebf 100644 --- a/docs/advanced-guide/overriding-default/page.md +++ b/docs/advanced-guide/overriding-default/page.md @@ -116,6 +116,33 @@ func listHandler(ctx *gofr.Context) (any, error) { } ``` +## HTTP Redirects + +GoFr allows redirecting HTTP requests to other URLs using the `response.Redirect` type. + +### Example + +```go +package main + +import ( + "gofr.dev/pkg/gofr" + + "gofr.dev/pkg/gofr/http/response" +) + +func main() { + app := gofr.New() + + app.GET("/old-page", func(ctx *gofr.Context) (any, error) { + // Redirect to a new URL + return response.Redirect{URL: "https://example.com/new-page"}, nil + }) + + app.Run() +} +``` + ## Favicon.ico By default, GoFr load its own `favicon.ico` present in root directory for an application. To override `favicon.ico` user diff --git a/pkg/gofr/http/responder.go b/pkg/gofr/http/responder.go index bce1ba734..99491bebb 100644 --- a/pkg/gofr/http/responder.go +++ b/pkg/gofr/http/responder.go @@ -27,17 +27,12 @@ type Responder struct { // Respond sends a response with the given data and handles potential errors, setting appropriate // status codes and formatting responses as JSON or raw data as needed. func (r Responder) Respond(data any, err error) { - statusCode, errorObj := r.determineResponse(data, err) - var resp any + switch v := data.(type) { - case resTypes.Raw: - resp = v.Data - case resTypes.Response: - resp = response{Data: v.Data, Metadata: v.Metadata, Error: errorObj} case resTypes.File: r.w.Header().Set("Content-Type", v.ContentType) - r.w.WriteHeader(statusCode) + r.w.WriteHeader(http.StatusOK) _, _ = r.w.Write(v.Content) @@ -47,6 +42,29 @@ func (r Responder) Respond(data any, err error) { v.Render(r.w) return + case resTypes.Redirect: + // HTTP 302 by default + statusCode := http.StatusFound + + switch r.method { + case http.MethodPost, http.MethodPut, http.MethodPatch: + // HTTP 303 + statusCode = http.StatusSeeOther + } + + r.w.Header().Set("Location", v.URL) + r.w.WriteHeader(statusCode) + + return + } + + statusCode, errorObj := r.determineResponse(data, err) + + switch v := data.(type) { + case resTypes.Raw: + resp = v.Data + case resTypes.Response: + resp = response{Data: v.Data, Metadata: v.Metadata, Error: errorObj} default: // handling where an interface contains a nullable type with a nil value. if isNil(data) { diff --git a/pkg/gofr/http/responder_test.go b/pkg/gofr/http/responder_test.go index e88ec8392..130692c2f 100644 --- a/pkg/gofr/http/responder_test.go +++ b/pkg/gofr/http/responder_test.go @@ -348,3 +348,39 @@ func removeTemplateDir(t *testing.T) { require.NoError(t, err) } + +func TestResponder_RedirectResponse_Post(t *testing.T) { + recorder := httptest.NewRecorder() + r := NewResponder(recorder, http.MethodPost) + + // Set up redirect with specific URL and status code + redirectURL := "/new-location?from=start" + statusCode := http.StatusSeeOther // 303 + + redirect := resTypes.Redirect{URL: redirectURL} + + r.Respond(redirect, nil) + + assert.Equal(t, statusCode, recorder.Code, "Redirect should set the correct status code") + assert.Equal(t, redirectURL, recorder.Header().Get("Location"), + "Redirect should set the Location header") + assert.Empty(t, recorder.Body.String(), "Redirect response should not have a body") +} + +func TestResponder_RedirectResponse_Head(t *testing.T) { + recorder := httptest.NewRecorder() + r := NewResponder(recorder, http.MethodHead) + + // Set up redirect with specific URL and status code + redirectURL := "/new-location?from=start" + statusCode := http.StatusFound // 302 + + redirect := resTypes.Redirect{URL: redirectURL} + + r.Respond(redirect, nil) + + assert.Equal(t, statusCode, recorder.Code, "Redirect should set the correct status code") + assert.Equal(t, redirectURL, recorder.Header().Get("Location"), + "Redirect should set the Location header") + assert.Empty(t, recorder.Body.String(), "Redirect response should not have a body") +} diff --git a/pkg/gofr/http/response/redirect.go b/pkg/gofr/http/response/redirect.go new file mode 100644 index 000000000..c6318d64d --- /dev/null +++ b/pkg/gofr/http/response/redirect.go @@ -0,0 +1,5 @@ +package response + +type Redirect struct { + URL string +}