forked from TrueCloudLab/certificates
optimized render.JSON (#929)
* api/render: render JSON directly to the underlying writer * also consider json.MarshalerError a panic
This commit is contained in:
parent
ef951f2075
commit
2139121683
2 changed files with 56 additions and 11 deletions
|
@ -2,7 +2,6 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -24,14 +23,25 @@ func JSON(w http.ResponseWriter, v interface{}) {
|
||||||
// JSONStatus sets the Content-Type of w to application/json unless one is
|
// JSONStatus sets the Content-Type of w to application/json unless one is
|
||||||
// specified.
|
// specified.
|
||||||
func JSONStatus(w http.ResponseWriter, v interface{}, status int) {
|
func JSONStatus(w http.ResponseWriter, v interface{}, status int) {
|
||||||
var b bytes.Buffer
|
|
||||||
if err := json.NewEncoder(&b).Encode(v); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setContentTypeUnlessPresent(w, "application/json")
|
setContentTypeUnlessPresent(w, "application/json")
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
_, _ = b.WriteTo(w)
|
|
||||||
|
if err := json.NewEncoder(w).Encode(v); err != nil {
|
||||||
|
var errUnsupportedType *json.UnsupportedTypeError
|
||||||
|
if errors.As(err, &errUnsupportedType) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errUnsupportedValue *json.UnsupportedValueError
|
||||||
|
if errors.As(err, &errUnsupportedValue) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errMarshalError *json.MarshalerError
|
||||||
|
if errors.As(err, &errMarshalError) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.EnabledResponse(w, v)
|
log.EnabledResponse(w, v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -26,10 +28,43 @@ func TestJSON(t *testing.T) {
|
||||||
assert.Empty(t, rw.Fields())
|
assert.Empty(t, rw.Fields())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJSONPanics(t *testing.T) {
|
func TestJSONPanicsOnUnsupportedType(t *testing.T) {
|
||||||
assert.Panics(t, func() {
|
jsonPanicTest[json.UnsupportedTypeError](t, make(chan struct{}))
|
||||||
JSON(httptest.NewRecorder(), make(chan struct{}))
|
}
|
||||||
})
|
|
||||||
|
func TestJSONPanicsOnUnsupportedValue(t *testing.T) {
|
||||||
|
jsonPanicTest[json.UnsupportedValueError](t, math.NaN())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONPanicsOnMarshalerError(t *testing.T) {
|
||||||
|
var v erroneousJSONMarshaler
|
||||||
|
jsonPanicTest[json.MarshalerError](t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type erroneousJSONMarshaler struct{}
|
||||||
|
|
||||||
|
func (erroneousJSONMarshaler) MarshalJSON() ([]byte, error) {
|
||||||
|
return nil, assert.AnError
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonPanicTest[T json.UnsupportedTypeError | json.UnsupportedValueError | json.MarshalerError](t *testing.T, v any) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
var err error
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Fatal("expected panic")
|
||||||
|
} else if e, ok := r.(error); !ok {
|
||||||
|
t.Fatalf("did not panic with an error (%T)", r)
|
||||||
|
} else {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
|
||||||
|
var e *T
|
||||||
|
assert.ErrorAs(t, err, &e)
|
||||||
|
}()
|
||||||
|
|
||||||
|
JSON(httptest.NewRecorder(), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type renderableError struct {
|
type renderableError struct {
|
||||||
|
|
Loading…
Reference in a new issue