add rest package
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadRequestJSON reads the body of an HTTP request into a target reference.
|
||||||
|
func ReadRequestJSON(req *http.Request, v any) error {
|
||||||
|
data, err := io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteResponseJSON writes an HTTP response as JSON.
|
||||||
|
func WriteResponseJSON(w http.ResponseWriter, statusCode int, data any) error {
|
||||||
|
b, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(statusCode)
|
||||||
|
w.Write(b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error represents a REST API error.
|
||||||
|
// It can be marshaled to JSON with ease.
|
||||||
|
type Error struct {
|
||||||
|
StatusCode int `json:"statusCode"` // HTTP status code (200, 404, 500 etc.)
|
||||||
|
Message string `json:"message"` // Status message ("OK", "Not found", "Internal server error" etc.)
|
||||||
|
Data map[string]interface{} `json:"data,omitempty"` // Optional additional data.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error retrieves the message of a REST API Error.
|
||||||
|
// If it has a "error" string attached using WithData or WithError, that message is returned.
|
||||||
|
// Otherwise, the Error's own message is returned.
|
||||||
|
func (e Error) Error() string {
|
||||||
|
if e.Data != nil && e.Data["error"] != nil {
|
||||||
|
if value, ok := e.Data["error"].(string); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is determines whether the Error is an instance of the target.
|
||||||
|
// https://pkg.go.dev/errors#Is
|
||||||
|
//
|
||||||
|
// This function supports matching both any error and a specific error, based on the status code.
|
||||||
|
// Use rest.Err (an empty error) for the former:
|
||||||
|
//
|
||||||
|
// errors.Is(err, rest.Err) // True if any REST API Error
|
||||||
|
// errors.Is(err, rest.ErrBadRequest) // True if error is REST 400 Bad Request
|
||||||
|
func (e Error) Is(target error) bool {
|
||||||
|
t, ok := target.(Error)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.StatusCode == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return t.StatusCode == e.StatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithData returns a copy of the HTTP error with the given data merged in.
|
||||||
|
func (e Error) WithData(data map[string]interface{}) Error {
|
||||||
|
if e.Data == nil {
|
||||||
|
e.Data = map[string]any{}
|
||||||
|
}
|
||||||
|
if data != nil {
|
||||||
|
for key, value := range data {
|
||||||
|
e.Data[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError returns a copy of the HTTP error with the given error's message merged in to its additional data.
|
||||||
|
func (e Error) WithError(err error) Error {
|
||||||
|
return e.WithData(map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMessage returns a copy of the HTTP error with the given message.
|
||||||
|
func (e Error) WithMessage(message string) Error {
|
||||||
|
e.Message = message
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithValue returns a copy of the HTTP error with a single data value added.
|
||||||
|
func (e Error) WithValue(name string, value any) Error {
|
||||||
|
return e.WithData(map[string]any{
|
||||||
|
name: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the HTTP error to an HTTP response as plain text.
|
||||||
|
// Additional data is omitted.
|
||||||
|
func (e Error) Write(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(e.StatusCode)
|
||||||
|
w.Write([]byte(e.Message))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteJSON writes the HTTP error to an HTTP response as JSON.
|
||||||
|
func (e Error) WriteJSON(w http.ResponseWriter) error {
|
||||||
|
return WriteResponseJSON(w, e.StatusCode, e)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Err is an empty error, representing any REST API Error.
|
||||||
|
var Err = Error{}
|
||||||
|
|
||||||
|
// ErrBadRequest represents HTTP 400 Bad Request.
|
||||||
|
var ErrBadRequest = Error{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "Bad request",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInternalServerError represents HTTP 500 Internal Server Error.
|
||||||
|
var ErrInternalServerError = Error{
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
Message: "Internal server error",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNotFound represents HTTP 404 Not Found.
|
||||||
|
var ErrNotFound = Error{
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
Message: "Not found",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnavailable represents HTTP 503 Service Unavailable.
|
||||||
|
var ErrUnavailable = Error{
|
||||||
|
StatusCode: http.StatusServiceUnavailable,
|
||||||
|
Message: "Service unavailable",
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user