Skip to content

Commit 6772915

Browse files
authored
Merge pull request #13 from orsinium-labs/body-data
Make Read return Data, add ContentType middleware
2 parents 64f54f8 + c1a5a5f commit 6772915

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

josh.go

+11-11
Original file line numberDiff line numberDiff line change
@@ -70,31 +70,31 @@ func SetHeader(r Req, key headers.Header, value string) {
7070
// Read and parse request body as JSON.
7171
//
7272
// Accepts the expected value of "type" field and Req.Body.
73-
func Read[T any](t string, r io.Reader) (T, error) {
74-
// TODO: improve based on this blog post:
75-
// https://www.alexedwards.net/blog/how-to-properly-parse-a-json-request-body
73+
func Read[T any](t string, r io.Reader) (Data[T], error) {
7674
if r == nil {
77-
return *new(T), errors.New("request body is empty")
75+
return Data[T]{}, errors.New("request body is empty")
7876
}
7977
var v struct {
8078
Data *Data[T] `json:"data"`
8179
}
80+
r = io.LimitReader(r, 1024*1024)
8281
decoder := json.NewDecoder(r)
8382
decoder.DisallowUnknownFields()
8483
err := decoder.Decode(&v)
8584
if err != nil {
86-
return *new(T), err
85+
return Data[T]{}, err
8786
}
8887
if v.Data == nil {
89-
return *new(T), errors.New("data field not found in request body")
88+
return Data[T]{}, errors.New("data field not found in request body")
9089
}
91-
if v.Data.Type != t {
92-
return *new(T), fmt.Errorf("unexpected request type: expected %s, got %s", t, v.Data.Type)
90+
if t != "" && v.Data.Type != t {
91+
return *v.Data, fmt.Errorf("unexpected request type: expected %s, got %s", t, v.Data.Type)
9392
}
94-
if v.Data.ID != "" {
95-
return *new(T), errors.New("requests cannot contain id")
93+
err = decoder.Decode(&struct{}{})
94+
if !errors.Is(err, io.EOF) {
95+
return Data[T]{}, errors.New("request body contains more than one JSON object")
9696
}
97-
return v.Data.Attributes, nil
97+
return *v.Data, nil
9898
}
9999

100100
// Resp is a response type.

josh_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ func TestUnwrap(t *testing.T) {
6262

6363
func TestRead(t *testing.T) {
6464
h := josh.Wrap(func(r josh.Req) josh.Resp {
65-
msg, err := josh.Read[string]("foo", r.Body)
65+
body, err := josh.Read[string]("foo", r.Body)
6666
if err != nil {
6767
panic(err)
6868
}
69+
msg := body.Attributes
6970
msg = strings.ToUpper(msg)
7071
return josh.Ok(msg)
7172
})

middlewares/content_type.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package middlewares
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/orsinium-labs/josh"
8+
"github.com/orsinium-labs/josh/statuses"
9+
)
10+
11+
// Require the request to have a specific Content-Type.
12+
//
13+
// If "ct" is an empty string, the "application/vnd.api+json" will be required.
14+
func ContentType(ct string, h josh.Handler) josh.Handler {
15+
if ct == "" {
16+
ct = "application/vnd.api+json"
17+
}
18+
return func(r josh.Req) josh.Resp {
19+
act := r.Header.Get("Content-Type")
20+
mediaType := strings.ToLower(strings.TrimSpace(strings.Split(act, ";")[0]))
21+
if mediaType != ct {
22+
return josh.Resp{
23+
Status: statuses.UnsupportedMediaType,
24+
Errors: []josh.Error{{
25+
Title: "Unsupported Content-Type",
26+
Detail: fmt.Sprintf("The request must have %s Content-Type", ct),
27+
}},
28+
}
29+
}
30+
return h(r)
31+
}
32+
}

0 commit comments

Comments
 (0)