api: response error in json instead of html for better rendering flexibility

This commit is contained in:
yusing 2025-01-29 11:50:08 +08:00
parent 4ad6257dab
commit d9b6b82f07
3 changed files with 43 additions and 10 deletions

View file

@ -1,10 +1,10 @@
package utils package utils
import ( import (
"encoding/json"
"net/http" "net/http"
E "github.com/yusing/go-proxy/internal/error" E "github.com/yusing/go-proxy/internal/error"
"github.com/yusing/go-proxy/internal/logging"
"github.com/yusing/go-proxy/internal/utils/strutils/ansi" "github.com/yusing/go-proxy/internal/utils/strutils/ansi"
) )
@ -31,14 +31,13 @@ func RespondError(w http.ResponseWriter, err error, code ...int) {
if len(code) == 0 { if len(code) == 0 {
code = []int{http.StatusBadRequest} code = []int{http.StatusBadRequest}
} }
w.Header().Set("Content-Type", "text/html; charset=utf-8") buf, err := json.Marshal(err)
buf := make([]byte, 0, 100) if err != nil { // just in case
errMsg := err.Error() w.Header().Set("Content-Type", "text/plain; charset=utf-8")
buf, err = logging.FormatMessageToHTMLBytes(errMsg, buf) http.Error(w, ansi.StripANSI(err.Error()), code[0])
if err != nil {
http.Error(w, ansi.StripANSI(errMsg), code[0])
return return
} }
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(code[0]) w.WriteHeader(code[0])
_, _ = w.Write(buf) _, _ = w.Write(buf)
} }

View file

@ -1,6 +1,7 @@
package err package err
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
) )
@ -46,3 +47,18 @@ func (err baseError) Withf(format string, args ...any) Error {
func (err *baseError) Error() string { func (err *baseError) Error() string {
return err.Err.Error() return err.Err.Error()
} }
// MarshalJSON implements the json.Marshaler interface.
func (err *baseError) MarshalJSON() ([]byte, error) {
//nolint:errorlint
switch err := err.Err.(type) {
case Error, *withSubject:
return json.Marshal(err)
case json.Marshaler:
return err.MarshalJSON()
case interface{ MarshalText() ([]byte, error) }:
return err.MarshalText()
default:
return json.Marshal(err.Error())
}
}

View file

@ -1,6 +1,7 @@
package err package err
import ( import (
"encoding/json"
"strings" "strings"
"github.com/yusing/go-proxy/internal/utils/strutils/ansi" "github.com/yusing/go-proxy/internal/utils/strutils/ansi"
@ -8,8 +9,8 @@ import (
//nolint:errname //nolint:errname
type withSubject struct { type withSubject struct {
Subjects []string `json:"subjects"` Subjects []string
Err error `json:"err"` Err error
pendingSubject string pendingSubject string
} }
@ -74,7 +75,7 @@ func (err *withSubject) Error() string {
for _, s := range err.Subjects { for _, s := range err.Subjects {
size += len(s) size += len(s)
} }
sb.Grow(size + 2 + n*len(subjectSep) + len(errStr)) sb.Grow(size + 2 + n*len(subjectSep) + len(errStr) + len(highlight("")))
for i := n - 1; i > 0; i-- { for i := n - 1; i > 0; i-- {
sb.WriteString(err.Subjects[i]) sb.WriteString(err.Subjects[i])
@ -85,3 +86,20 @@ func (err *withSubject) Error() string {
sb.WriteString(errStr) sb.WriteString(errStr)
return sb.String() return sb.String()
} }
// MarshalJSON implements the json.Marshaler interface.
func (err *withSubject) MarshalJSON() ([]byte, error) {
subjects := make([]string, len(err.Subjects))
for i, s := range err.Subjects {
subjects[len(err.Subjects)-i-1] = s
}
reversed := struct {
Subjects []string `json:"subjects"`
Err error `json:"err"`
}{
Subjects: subjects,
Err: err.Err,
}
return json.Marshal(reversed)
}