Skip to content

Commit 138bfa0

Browse files
authored
openapi3filter: Fallback to string when decoding request parameters (#631)
1 parent fc05f1c commit 138bfa0

File tree

5 files changed

+92
-9
lines changed

5 files changed

+92
-9
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
[![Join Gitter Chat Channel -](https://badges.gitter.im/getkin/kin.svg)](https://gitter.im/getkin/kin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
55

66
# Introduction
7-
A [Go](https://golang.org) project for handling [OpenAPI](https://www.openapis.org/) files. We target the latest OpenAPI version (currently 3), but the project contains support for older OpenAPI versions too.
7+
A [Go](https://golang.org) project for handling [OpenAPI](https://www.openapis.org/) files. We target:
8+
* [OpenAPI `v2.0`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md) (formerly known as Swagger)
9+
* [OpenAPI `v3.0`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)
10+
* [OpenAPI `v3.1`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md) Soon! [Tracking issue here.](https://github.com/getkin/kin-openapi/issues/230)
811

912
Licensed under the [MIT License](./LICENSE).
1013

openapi3/loader.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ type Loader struct {
5656

5757
// NewLoader returns an empty Loader
5858
func NewLoader() *Loader {
59-
return &Loader{}
59+
return &Loader{
60+
Context: context.Background(),
61+
}
6062
}
6163

6264
func (loader *Loader) resetVisitedPathItemRefs() {

openapi3filter/issue624_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package openapi3filter
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/getkin/kin-openapi/openapi3"
10+
"github.com/getkin/kin-openapi/routers/gorillamux"
11+
)
12+
13+
func TestIssue624(t *testing.T) {
14+
loader := openapi3.NewLoader()
15+
ctx := loader.Context
16+
spec := `
17+
openapi: 3.0.0
18+
info:
19+
version: 1.0.0
20+
title: Sample API
21+
paths:
22+
/items:
23+
get:
24+
description: Returns a list of stuff
25+
parameters:
26+
- description: "test non object"
27+
explode: true
28+
style: form
29+
in: query
30+
name: test
31+
required: false
32+
content:
33+
application/json:
34+
schema:
35+
anyOf:
36+
- type: string
37+
- type: integer
38+
responses:
39+
'200':
40+
description: Successful response
41+
`[1:]
42+
43+
doc, err := loader.LoadFromData([]byte(spec))
44+
require.NoError(t, err)
45+
46+
err = doc.Validate(ctx)
47+
require.NoError(t, err)
48+
49+
router, err := gorillamux.NewRouter(doc)
50+
require.NoError(t, err)
51+
httpReq, err := http.NewRequest(http.MethodGet, `/items?test=test1`, nil)
52+
require.NoError(t, err)
53+
54+
route, pathParams, err := router.FindRoute(httpReq)
55+
require.NoError(t, err)
56+
57+
requestValidationInput := &RequestValidationInput{
58+
Request: httpReq,
59+
PathParams: pathParams,
60+
Route: route,
61+
}
62+
err = ValidateRequest(ctx, requestValidationInput)
63+
require.NoError(t, err)
64+
}

openapi3filter/req_resp_decoder.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,10 @@ func decodeContentParameter(param *openapi3.Parameter, input *RequestValidationI
158158
}
159159

160160
func defaultContentParameterDecoder(param *openapi3.Parameter, values []string) (
161-
outValue interface{}, outSchema *openapi3.Schema, err error) {
161+
outValue interface{},
162+
outSchema *openapi3.Schema,
163+
err error,
164+
) {
162165
// Only query parameters can have multiple values.
163166
if len(values) > 1 && param.In != openapi3.ParameterInQuery {
164167
err = fmt.Errorf("%s parameter %q cannot have multiple values", param.In, param.Name)
@@ -170,7 +173,6 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string)
170173
err = fmt.Errorf("parameter %q expected to have content", param.Name)
171174
return
172175
}
173-
174176
// We only know how to decode a parameter if it has one content, application/json
175177
if len(content) != 1 {
176178
err = fmt.Errorf("multiple content types for parameter %q", param.Name)
@@ -184,16 +186,28 @@ func defaultContentParameterDecoder(param *openapi3.Parameter, values []string)
184186
}
185187
outSchema = mt.Schema.Value
186188

189+
unmarshal := func(encoded string) (decoded interface{}, err error) {
190+
if err = json.Unmarshal([]byte(encoded), &decoded); err != nil {
191+
const specialJSONChars = `[]{}":,`
192+
if !strings.ContainsAny(encoded, specialJSONChars) {
193+
// A string in a query parameter is not serialized with (double) quotes
194+
// as JSON would expect, so let's fallback to that.
195+
decoded, err = encoded, nil
196+
}
197+
}
198+
return
199+
}
200+
187201
if len(values) == 1 {
188-
if err = json.Unmarshal([]byte(values[0]), &outValue); err != nil {
202+
if outValue, err = unmarshal(values[0]); err != nil {
189203
err = fmt.Errorf("error unmarshaling parameter %q", param.Name)
190204
return
191205
}
192206
} else {
193207
outArray := make([]interface{}, 0, len(values))
194208
for _, v := range values {
195209
var item interface{}
196-
if err = json.Unmarshal([]byte(v), &item); err != nil {
210+
if item, err = unmarshal(v); err != nil {
197211
err = fmt.Errorf("error unmarshaling parameter %q", param.Name)
198212
return
199213
}

openapi3filter/validate_request.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
148148
value = schema.Default
149149
req := input.Request
150150
switch parameter.In {
151-
// case openapi3.ParameterInPath: TODO: no idea how to handle this
151+
case openapi3.ParameterInPath:
152+
// Path parameters are required.
153+
// Next check `parameter.Required && !found` will catch this.
152154
case openapi3.ParameterInQuery:
153155
q := req.URL.Query()
154156
q.Add(parameter.Name, fmt.Sprintf("%v", value))
@@ -160,8 +162,6 @@ func ValidateParameter(ctx context.Context, input *RequestValidationInput, param
160162
Name: parameter.Name,
161163
Value: fmt.Sprintf("%v", value),
162164
})
163-
default:
164-
return fmt.Errorf("unsupported parameter's 'in': %s", parameter.In)
165165
}
166166
}
167167

0 commit comments

Comments
 (0)