这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ The following types are supported out of the box:

For example, if the parameter is a query param and the type is `[]string` it might look like `?tags=tag1,tag2` in the URI.

The special struct field `Body` will be treated as the input request body and can refer to another struct or you can embed a struct inline.
The special struct field `Body` will be treated as the input request body and can refer to another struct or you can embed a struct inline. `RawBody` can also be used to provide access to the `[]byte` used to validate & load `Body`.

Here is an example:

Expand Down Expand Up @@ -472,6 +472,18 @@ op.NoBodyReadTimeout()
op.Run(...)
```

If you just need access to the input body bytes and still want to use the built-in JSON Schema validation, then you can instead use the `RawBody` input struct field.

```go
type MyBody struct {
// This will generate JSON Schema, validate the input, and parse it.
Body MyStruct

// This will contain the raw bytes used to load the above.
RawBody []byte
}
```

### Resolvers

Sometimes the built-in validation isn't sufficient for your use-case, or you want to do something more complex with the incoming request object. This is where resolvers come in.
Expand Down
6 changes: 6 additions & 0 deletions resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ func setFields(ctx *hcontext, req *http.Request, input reflect.Value, t reflect.
Value: string(data),
})
}

// If requested, also provide access to the raw body bytes.
if _, ok := t.FieldByName("RawBody"); ok {
input.FieldByName("RawBody").Set(reflect.ValueOf(data))
}

continue
}

Expand Down
33 changes: 33 additions & 0 deletions resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,36 @@ func TestStringQueryEmpty(t *testing.T) {
assert.Equal(t, o.BooleanParam, true)
assert.Equal(t, o.OtherParam, "")
}

func TestRawBody(t *testing.T) {
app := newTestRouter()

app.Resource("/").Get("test", "Test",
NewResponse(http.StatusOK, "desc"),
).Run(func(ctx Context, input struct {
Body struct {
Name string `json:"name"`
Tags []string `json:"tags"`
}
RawBody []byte
}) {
ctx.Write(input.RawBody)
})

// Note the weird formatting
body := `{ "name" : "Huma","tags": [ "one" ,"two"]}`

w := httptest.NewRecorder()
r, _ := http.NewRequest(http.MethodGet, "/", strings.NewReader(body))
app.ServeHTTP(w, r)

assert.Equal(t, http.StatusOK, w.Result().StatusCode)
assert.Equal(t, body, w.Body.String())

// Invalid input should still fail validation!
w = httptest.NewRecorder()
r, _ = http.NewRequest(http.MethodGet, "/", strings.NewReader("{}"))
app.ServeHTTP(w, r)

assert.Equal(t, http.StatusUnprocessableEntity, w.Result().StatusCode)
}
5 changes: 5 additions & 0 deletions responses/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ func RequestEntityTooLarge() huma.Response {
return errorResponse(http.StatusRequestEntityTooLarge)
}

// UnsupportedMediaType HTTP 415 response with a structured error body (e.g. JSON).
func UnsupportedMediaType() huma.Response {
return errorResponse(http.StatusUnsupportedMediaType)
}

// UnprocessableEntity HTTP 422 response with a structured error body (e.g. JSON).
func UnprocessableEntity() huma.Response {
return errorResponse(http.StatusUnprocessableEntity)
Expand Down
2 changes: 2 additions & 0 deletions responses/responses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var funcs = struct {
RequestTimeout,
Conflict,
PreconditionFailed,
UnsupportedMediaType,
RequestEntityTooLarge,
UnprocessableEntity,
PreconditionRequired,
Expand Down Expand Up @@ -72,6 +73,7 @@ func TestResponses(t *testing.T) {
http.StatusConflict,
http.StatusPreconditionFailed,
http.StatusRequestEntityTooLarge,
http.StatusUnsupportedMediaType,
http.StatusUnprocessableEntity,
http.StatusPreconditionRequired,
http.StatusInternalServerError,
Expand Down