1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
// Package openapi3filter validates that requests and inputs request an OpenAPI 3 specification file.
package openapi3filter
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func ValidateResponse(c context.Context, input *ResponseValidationInput) error {
req := input.RequestValidationInput.Request
switch req.Method {
case "HEAD":
return nil
}
status := input.Status
if status < 100 {
return &ResponseError{
Input: input,
Reason: "illegal status code",
Err: fmt.Errorf("Status %d", status),
}
}
// These status codes will never be validated.
// TODO: The list is probably missing some.
switch status {
case http.StatusNotModified,
http.StatusPermanentRedirect,
http.StatusTemporaryRedirect,
http.StatusMovedPermanently:
return nil
}
route := input.RequestValidationInput.Route
options := input.Options
if options == nil {
options = DefaultOptions
}
// Find input for the current status
responses := route.Operation.Responses
if responses != nil && len(responses) > 0 {
responseRef := responses.Get(status) // Response
if responseRef == nil {
responseRef = responses.Default() // Default input
}
if responseRef == nil {
// By default, status that is not documented is allowed
if !options.IncludeResponseStatus {
return nil
}
// Other.
return &ResponseError{
Input: input,
Reason: "status is not supported",
}
}
response := responseRef.Value
if response == nil {
return &ResponseError{
Input: input,
Reason: "response has not been resolved",
}
}
content := response.Content
if len(content) > 0 && !options.ExcludeResponseBody {
inputMIME := input.Header.Get("Content-Type")
mediaType := parseMediaType(inputMIME)
contentType := content[mediaType]
if contentType == nil {
return &ResponseError{
Input: input,
Reason: "input header 'Content-type' has unexpected value",
}
}
schemaRef := contentType.Schema
if schemaRef != nil && isMediaTypeJSON(mediaType) {
schema := schemaRef.Value
// Read request body
body := input.Body
// Response would contain partial or empty input body
// after we begin reading.
// Ensure that this doesn't happen.
input.Body = nil
// Ensure we close the reader
defer body.Close()
// Read all
data, err := ioutil.ReadAll(body)
if err != nil {
return &ResponseError{
Input: input,
Reason: "reading the input body failed",
Err: err,
}
}
// Put the data back into the request
input.SetBodyBytes(data)
// Decode JSON
var value interface{}
if err := json.Unmarshal(data, &value); err != nil {
return &ResponseError{
Input: input,
Reason: "decoding JSON in the input body failed",
Err: err,
}
}
// Validate JSON with the schema
if err := schema.VisitJSON(value); err != nil {
return &ResponseError{
Input: input,
Err: err,
}
}
}
}
}
return nil
}
|