httpx: Add new redirect
This commit is contained in:
parent
41d41035e3
commit
8e2d2cce3e
5 changed files with 123 additions and 35 deletions
75
pkg/httpx/errors.go
Normal file
75
pkg/httpx/errors.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package httpx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrInterceptor intercepts errors and directly returns specific responses for them
|
||||
type ErrInterceptor struct {
|
||||
Errors map[error]Response
|
||||
Fallback Response
|
||||
}
|
||||
|
||||
// Intercept attempts to intercept the given error.
|
||||
// When err is nil, does nothing.
|
||||
//
|
||||
// When err is not nil, first attempts to find a static response in errors and respond with that.
|
||||
// Otherwise it returns the Fallback response.
|
||||
// intercepted indicates if some response was sent.
|
||||
func (ei ErrInterceptor) Intercept(w http.ResponseWriter, r *http.Request, err error) (intercepted bool) {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
res, ok := ei.Errors[err]
|
||||
if !ok {
|
||||
res = ei.Fallback
|
||||
}
|
||||
|
||||
res.ServerHTTP(w, r)
|
||||
return true
|
||||
}
|
||||
|
||||
// StatusInterceptor creates a new ErrInterceptor handling default responses.
|
||||
// If body returns err != nil, StatusInterceptor calls panic().
|
||||
func StatusInterceptor(contentType string, body func(code int, text string) ([]byte, error)) ErrInterceptor {
|
||||
makeResponse := func(code int) (res Response) {
|
||||
var err error
|
||||
res.Body, err = body(code, http.StatusText(code))
|
||||
if err != nil {
|
||||
panic("StatusInterceptor: err != nil")
|
||||
}
|
||||
|
||||
res.ContentType = contentType
|
||||
res.StatusCode = code
|
||||
return
|
||||
}
|
||||
|
||||
return ErrInterceptor{
|
||||
Errors: map[error]Response{
|
||||
ErrNotFound: makeResponse(http.StatusNotFound),
|
||||
ErrForbidden: makeResponse(http.StatusForbidden),
|
||||
},
|
||||
Fallback: makeResponse(http.StatusInternalServerError),
|
||||
}
|
||||
}
|
||||
|
||||
// Common errors accepted by all httpx handlers
|
||||
var (
|
||||
ErrNotFound = errors.New("httpx: Not Found")
|
||||
ErrForbidden = errors.New("httpx: Forbidden")
|
||||
)
|
||||
|
||||
var (
|
||||
textInterceptor = StatusInterceptor("text/plain", func(code int, text string) ([]byte, error) {
|
||||
return []byte(text), nil
|
||||
})
|
||||
jsonInterceptor = StatusInterceptor("application/json", func(code int, text string) ([]byte, error) {
|
||||
return json.Marshal(map[string]any{"status": text, "code": code})
|
||||
})
|
||||
htmlInterceptor = StatusInterceptor("text/html", func(code int, text string) ([]byte, error) {
|
||||
return []byte(`<!DOCTYPE HTML><title>` + text + `</title>` + text), nil
|
||||
})
|
||||
)
|
||||
|
|
@ -12,27 +12,13 @@ type HTMLHandler[T any] struct {
|
|||
TemplateName string // name of template to render, defaults to root
|
||||
}
|
||||
|
||||
var htmlInternalServerErr = []byte(`<!DOCTYPE HTML><title>Internal Server Error</title>Internal Server Error`)
|
||||
var htmlNotFound = []byte(`<!DOCTYPE HTML><title>Not Found</title>Not Found`)
|
||||
|
||||
// ServeHTTP calls j(r) and returns json
|
||||
func (h HTMLHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// call the function
|
||||
result, err := h.Handler(r)
|
||||
|
||||
// entity not found
|
||||
if err == ErrNotFound {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write(htmlNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// handle other errors
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write(htmlInternalServerErr)
|
||||
// intercept any errors
|
||||
if htmlInterceptor.Intercept(w, r, err) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,25 @@
|
|||
package httpx
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrNotFound should be returned from any httpx error to indicate that the item was not found
|
||||
var ErrNotFound = errors.New("httpx: Error 404")
|
||||
// Response represents a response to an http request.
|
||||
type Response struct {
|
||||
ContentType string // defaults to text/plain
|
||||
Body []byte
|
||||
StatusCode int // defaults to [http.StatusOK]
|
||||
}
|
||||
|
||||
func (response Response) ServerHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if response.ContentType == "" {
|
||||
response.ContentType = "text/plain"
|
||||
}
|
||||
w.Header().Set("Content-Type", response.ContentType)
|
||||
|
||||
if response.StatusCode <= 0 {
|
||||
response.StatusCode = http.StatusOK
|
||||
}
|
||||
w.WriteHeader(response.StatusCode)
|
||||
w.Write(response.Body)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
var jsonInternalServerErr = []byte(`{"status":"internal server error"}`)
|
||||
var jsonNotFound = []byte(`{"status":"not found"}`)
|
||||
|
||||
// JSON creates a new JSONHandler
|
||||
func JSON[T any](f func(r *http.Request) (T, error)) JSONHandler[T] {
|
||||
return JSONHandler[T](f)
|
||||
|
|
@ -22,19 +19,8 @@ func (j JSONHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
// call the function
|
||||
result, err := j(r)
|
||||
|
||||
// entity not found
|
||||
if err == ErrNotFound {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write(jsonNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// handle other errors
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write(jsonInternalServerErr)
|
||||
// handle any errors
|
||||
if jsonInterceptor.Intercept(w, r, err) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
22
pkg/httpx/redirect.go
Normal file
22
pkg/httpx/redirect.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package httpx
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RedirectHandler represents a handler that redirects the user to the address returned
|
||||
type RedirectHandler func(r *http.Request) (string, int, error)
|
||||
|
||||
// ServeHTTP calls r(r) and returns json
|
||||
func (rh RedirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// call the function
|
||||
url, code, err := rh(r)
|
||||
|
||||
// intercept the errors
|
||||
if textInterceptor.Intercept(w, r, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// do the redirect
|
||||
http.Redirect(w, r, url, code)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue