Selaa lähdekoodia

add rest package

Aneurin Barker Snook 1 vuosi sitten
commit
1fbda13eda
4 muutettua tiedostoa jossa 150 lisäystä ja 0 poistoa
  1. 28 0
      body.go
  2. 89 0
      error.go
  3. 30 0
      errors.go
  4. 3 0
      go.mod

+ 28 - 0
body.go

@@ -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
+}

+ 89 - 0
error.go

@@ -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)
+}

+ 30 - 0
errors.go

@@ -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",
+}

+ 3 - 0
go.mod

@@ -0,0 +1,3 @@
+module github.com/recipeer/go/rest
+
+go 1.21.2