diff --git a/openapi3/parameter.go b/openapi3/parameter.go index 9124d92a4..c55af474d 100644 --- a/openapi3/parameter.go +++ b/openapi3/parameter.go @@ -90,7 +90,7 @@ func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOpt } // Parameter is specified by OpenAPI/Swagger 3.0 standard. -// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameterObject +// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object type Parameter struct { ExtensionProps `json:"-" yaml:"-"` diff --git a/openapi3filter/issue201_test.go b/openapi3filter/issue201_test.go index 8b2b99d0e..0d363a17e 100644 --- a/openapi3filter/issue201_test.go +++ b/openapi3filter/issue201_test.go @@ -94,7 +94,7 @@ paths: }, "invalid required header": { - err: `response header "X-Blup" doesn't match the schema: string "bluuuuuup" doesn't match the regular expression "^blup$"`, + err: `response header "X-Blup" doesn't match schema: string "bluuuuuup" doesn't match the regular expression "^blup$"`, headers: map[string]string{ "X-Blip": "blip", "x-blop": "blop", diff --git a/openapi3filter/validate_response.go b/openapi3filter/validate_response.go index 27bef82d3..9804949a7 100644 --- a/openapi3filter/validate_response.go +++ b/openapi3filter/validate_response.go @@ -81,21 +81,9 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error for _, k := range headers { s := response.Headers[k] h := input.Header.Get(k) - if h == "" { - if s.Value.Required { - return &ResponseError{ - Input: input, - Reason: fmt.Sprintf("response header %q missing", k), - } - } - continue - } - if err := s.Value.Schema.Value.VisitJSON(h, opts...); err != nil { - return &ResponseError{ - Input: input, - Reason: fmt.Sprintf("response header %q doesn't match the schema", k), - Err: err, - } + err := validateResponseHeader(k, h, s, input, opts) + if err != nil { + return err } } @@ -171,6 +159,58 @@ func ValidateResponse(ctx context.Context, input *ResponseValidationInput) error return nil } +func validateResponseHeader( + headerName string, + headerVal string, + s *openapi3.HeaderRef, + input *ResponseValidationInput, + opts []openapi3.SchemaValidationOption, +) error { + if headerVal == "" && s.Value.Required { + return &ResponseError{ + Input: input, + Reason: fmt.Sprintf("response header %q missing", headerName), + } + } + + sm, err := s.Value.SerializationMethod() + if err != nil { + return &ResponseError{ + Input: input, + Reason: fmt.Sprintf("unable to get header %q serialization method", headerName), + Err: err, + } + } + + var decodedValue interface{} + dec := &headerParamDecoder{header: input.Header} + found := false + if decodedValue, found, err = decodeValue(dec, headerName, sm, s.Value.Schema, s.Value.Required); err != nil { + return &ResponseError{ + Input: input, + Reason: fmt.Sprintf("unable to decode header %q value", headerName), + Err: err, + } + } + + if found { + // If the value was found but not decoded it means there is no + // schema.Type defining how to interpret the header value + if decodedValue == nil { + // revert to the header's original value + decodedValue = headerVal + } + if err := s.Value.Schema.Value.VisitJSON(decodedValue, opts...); err != nil { + return &ResponseError{ + Input: input, + Reason: fmt.Sprintf("response header %q doesn't match schema", headerName), + Err: err, + } + } + } + return nil +} + // getSchemaIdentifier gets something by which a schema could be identified. // A schema by itself doesn't have a true identity field. This function makes // a best effort to get a value that can fill that void.