Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
9870d87d41 | |||
15887d9d2e | |||
e8aa16622b | |||
4612ee993e | |||
9b6664399f | |||
2bc73d2262 |
7 changed files with 126 additions and 4 deletions
|
@ -1,4 +1,4 @@
|
|||
package http
|
||||
package webutils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package http
|
||||
package webutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package http
|
||||
package webutils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
@ -9,8 +10,63 @@ import (
|
|||
// The error will have the given return code `code`
|
||||
// and a json encoded body with the field "id" set to `errId`
|
||||
// and a field "message" set to the `message`
|
||||
//
|
||||
// Deprecated: Use ProblemDetails or ProblemDetailsStatusOnly instead
|
||||
func HttpErr(w http.ResponseWriter, errId int, message string, code int) {
|
||||
w.WriteHeader(code)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, "{\"id\": %d, \"message\": \"%s\"}", errId, message)
|
||||
}
|
||||
|
||||
// Write an RFC 9457 compliant problem details response
|
||||
// If details is not nil, it will be included.
|
||||
// If extras is not nil, each key-value pair will be included at
|
||||
// the root layer.
|
||||
// Keys in extras that would overwrite the default elements will be ignored.
|
||||
// Those would be "type", "status", "title" and "detail"
|
||||
func ProblemDetails(
|
||||
w http.ResponseWriter,
|
||||
statusCode int,
|
||||
errorType string,
|
||||
errorTitle string,
|
||||
details *string,
|
||||
extras map[string]any,
|
||||
) {
|
||||
w.Header().Add("Content-Type", "application/problem+json")
|
||||
w.WriteHeader(statusCode)
|
||||
data := map[string]any{
|
||||
"type": errorType,
|
||||
"status": statusCode,
|
||||
"title": errorTitle,
|
||||
}
|
||||
if details != nil {
|
||||
data["detail"] = *details
|
||||
}
|
||||
if extras != nil {
|
||||
for k, v := range extras {
|
||||
if _, ok := data[k]; ok {
|
||||
// Don't overwrite default fields
|
||||
continue
|
||||
}
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
enc := json.NewEncoder(w)
|
||||
enc.Encode(data)
|
||||
}
|
||||
|
||||
// Write a simple problem details response.
|
||||
// It only provides the status code, as defined in RFC 9457, section 4.2.1
|
||||
func ProblemDetailsStatusOnly(w http.ResponseWriter, statusCode int) {
|
||||
w.Header().Add("Content-Type", "application/problem+json")
|
||||
w.WriteHeader(statusCode)
|
||||
data := map[string]any{
|
||||
"type": "about:blank",
|
||||
"title": http.StatusText(statusCode),
|
||||
"status": statusCode,
|
||||
"reference": "RFC 9457",
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.Encode(data)
|
||||
}
|
||||
|
|
17
http/json.go
Normal file
17
http/json.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package webutils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func SendJson(w http.ResponseWriter, data any) error {
|
||||
encoded, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprint(w, string(encoded))
|
||||
return nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package http
|
||||
package webutils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -9,6 +9,33 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func BuildLoggingMiddleware(extras map[string]string) HandlerBuilder {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return ChainMiddlewares(h,
|
||||
hlog.NewHandler(log.Logger),
|
||||
hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
|
||||
if strings.HasPrefix(r.URL.Path, "/assets") {
|
||||
return
|
||||
}
|
||||
logger := hlog.FromRequest(r).Info().
|
||||
Str("method", r.Method).
|
||||
Stringer("url", r.URL).
|
||||
Int("status", status).
|
||||
Int("size", size).
|
||||
Dur("duration", duration)
|
||||
for k, v := range extras {
|
||||
logger = logger.Str(k, v)
|
||||
}
|
||||
logger.Send()
|
||||
}),
|
||||
hlog.RemoteAddrHandler("ip"),
|
||||
hlog.UserAgentHandler("user_agent"),
|
||||
hlog.RefererHandler("referer"),
|
||||
hlog.RequestIDHandler("req_id", "Request-Id"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func LoggingMiddleware(handler http.Handler) http.Handler {
|
||||
return ChainMiddlewares(handler,
|
||||
hlog.NewHandler(log.Logger),
|
||||
|
|
13
math/math.go
Normal file
13
math/math.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package mathutils
|
||||
|
||||
type SignedNumber interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
|
||||
}
|
||||
|
||||
func Abs[T SignedNumber](num T) T {
|
||||
if num > 0 {
|
||||
return num
|
||||
} else {
|
||||
return num * -1
|
||||
}
|
||||
}
|
|
@ -81,6 +81,15 @@ func Contains[T comparable](a []T, b T) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func ContainsFunc[T any](a []T, f func(t T) bool) bool {
|
||||
for _, v := range a {
|
||||
if f(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Compact[T any](a []T, compactor func(acc T, next T) T) T {
|
||||
var acc T
|
||||
for _, v := range a {
|
||||
|
|
Loading…
Reference in a new issue