You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.8 KiB
Go
111 lines
3.8 KiB
Go
1 week ago
|
package internal
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"log/slog"
|
||
|
"net/http"
|
||
|
"runtime/debug"
|
||
|
)
|
||
|
|
||
|
// encode writes a JSON-encoded response to the provided http.ResponseWriter.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// - w: The http.ResponseWriter where the response will be written.
|
||
|
// - r: The *http.Request associated with the response. (Unused in this function but could be relevant for context.)
|
||
|
// - status: The HTTP status code to send with the response.
|
||
|
// - v: The value to encode and send as the JSON response. Can be of any type.
|
||
|
//
|
||
|
// Returns:
|
||
|
// - An error if the JSON encoding or writing to the ResponseWriter fails, otherwise nil.
|
||
|
func encode[T any](w http.ResponseWriter, r *http.Request, status int, v T) error {
|
||
|
w.Header().Set("Content-Type", "application/json")
|
||
|
w.WriteHeader(status)
|
||
|
if err := json.NewEncoder(w).Encode(v); err != nil {
|
||
|
return fmt.Errorf("encode json: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// decode reads and decodes a JSON-encoded request body into a value of the specified type.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// - r: The *http.Request containing the JSON-encoded body to decode.
|
||
|
//
|
||
|
// Returns:
|
||
|
// - The decoded value of type T.
|
||
|
// - An error if decoding fails, or if the body contains invalid JSON.
|
||
|
func decode[T any](r *http.Request) (T, error) {
|
||
|
var v T
|
||
|
if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
|
||
|
return v, fmt.Errorf("decode json: %w", err)
|
||
|
}
|
||
|
return v, nil
|
||
|
}
|
||
|
|
||
|
// serverError logs an internal server error and sends a 500 Internal Server Error
|
||
|
// response to the client.
|
||
|
//
|
||
|
// Usage:
|
||
|
// This function is intended for use when handling unexpected server-side errors.
|
||
|
// It logs the error details along with the HTTP request method, URI, and a stack
|
||
|
// trace for debugging purposes. After logging, it sends a standardized 500 Internal
|
||
|
// Server Error response to the client.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// - logger: The *slog.Logger instance used for logging error details.
|
||
|
// - w: The http.ResponseWriter to send the response to.
|
||
|
// - r: The *http.Request that triggered the error. Used to extract method and URI.
|
||
|
// - err: The error instance to log, providing context about the issue.
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// func handler(w http.ResponseWriter, r *http.Request) {
|
||
|
// err := someOperation()
|
||
|
// if err != nil {
|
||
|
// serverError(logger, w, r, err)
|
||
|
// return
|
||
|
// }
|
||
|
// // Handle request normally
|
||
|
// }
|
||
|
func serverError(logger *slog.Logger, w http.ResponseWriter, r *http.Request, err error) {
|
||
|
var (
|
||
|
method = r.Method
|
||
|
uri = r.URL.RequestURI()
|
||
|
// Use debug.Stack() to get the stack trace. This returns a byte slice, which
|
||
|
// we need to convert to a string so that it's readable in the log entry.
|
||
|
trace = string(debug.Stack())
|
||
|
)
|
||
|
|
||
|
logger.Error(err.Error(), "method", method, "uri", uri, "trace", trace)
|
||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||
|
}
|
||
|
|
||
|
// clientError sends an HTTP error response to the client with the specified
|
||
|
// status code and its corresponding description.
|
||
|
//
|
||
|
// Usage:
|
||
|
// This function is useful for handling HTTP errors in a consistent manner.
|
||
|
// It sends the appropriate HTTP status text as the response body, along with
|
||
|
// the given status code as the response status. This is typically used in web
|
||
|
// applications to return standardized error messages for various client-side
|
||
|
// errors.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// - w: The http.ResponseWriter to send the response to.
|
||
|
// - status: The HTTP status code to return. It should be a valid HTTP status
|
||
|
// code as defined in the http package.
|
||
|
//
|
||
|
// Example:
|
||
|
//
|
||
|
// func handler(w http.ResponseWriter, r *http.Request) {
|
||
|
// if !isAuthorized(r) {
|
||
|
// clientError(w, http.StatusForbidden) // Responds with "403 Forbidden"
|
||
|
// return
|
||
|
// }
|
||
|
// // Handle request normally
|
||
|
// }
|
||
|
func clientError(w http.ResponseWriter, status int) {
|
||
|
http.Error(w, http.StatusText(status), status)
|
||
|
}
|