OpenAPI-Specification
OpenAPI-Specification copied to clipboard
Do we need to clarify what a response missing content means
I've been writing up guides for contract testing with OpenAPI in a bunch of different languages, and the Ruby on Rails one using https://github.com/mkon/openapi_contracts/ gave this great example, letting me know an empty response has been defined, but a response has been provided by the API implementation.
1) widgets POST /widgets responds with 201 when valid
Failure/Error: expect(response).to match_openapi_doc(OPENAPI_DOC)
* Expected empty response body
# ./spec/requests/widgets_spec.rb:17:in `block (3 levels) in <top (required)>'
Here's some OpenAPI for visual people:
post:
summary: Create Widget
operationId: create-widget
requestBody:
description: Widget to create
required: true
content:
application/json:
schema:
$ref: "../components/schema/widget.yaml"
responses:
'201':
description: Created
Does this mean "There is definitely no content" or "I haven't bothered defining it but dont worry about it"?
There are ways to define a "dont worry about it" thats a bit more specific, like:
post:
summary: Create Widget
operationId: create-widget
requestBody:
description: Widget to create
required: true
content:
application/json:
schema:
$ref: "../components/schema/widget.yaml"
responses:
'201':
description: Created
content:
application/json:
schema:
type: object
Or you can pop an example in instead of worrying about defining a schema but still providing something tangible for docs/mocks to think about.
Scrabbling round the spec I coudn't find anything in 3.0 or 3.1 but the SmartBear Guide on Describing Responses does explicitly say:
Some responses, such as 204 No Content, have no body. To indicate the response body is empty, do not specify a content for the response:
This leads me to think all responses missing a content should have no body, because otherwise there's going to be some weird logic going "If 204 then missing means definitely no body but otherwise it just means I dunno!"
Would that be a breaking change to 3.1.x if its clarifying intent? Or does this have to go to Moonwalk?
Thanks, @philsturgeon ! See also:
- #2236
- #3528
- #3529
Hi! I need the same clarification:
Does this mean "There is definitely no content" or "I haven't bothered defining it but don't worry about it"?
I'm working on some scaffolding tools that have to extract meaning from these kinds of situations. My original interpretation was "will have a response with something" but that obviously breaks the intention when 204 - No Content is meant.
So:
- How can we specify "There's no content on this response"?
- How can we specift "This response may have anything as its contents"?
- What's the interpretation for a
content:-less response?
I saw the discussion at #2236 but that's outside my current scope because we are not intending to support optionally present responses (yet).
How can we specify "There's no content on this response"?
It depends on what you mean...
- Do you mean "There must not be a Content-Type response header"? If so, you can specify that, by defining a header with a false schema.
- Do you mean "There must not be a Content-Length response header"? If so, see above.
- Do you mean "Okay, there could be a Content-Length header, but if it exists, it must be zero"? If so, you can specify that too, with
"required": falseand"schema": "const": "0".
Discussion in TDC this week, we don't think we can infer from an absence of response information in an API description whether this means there is no response expected, or the response information is missing from the API description file.
How can we specift "This response may have anything as its contents"?
I would do that with...
responses:
2xx:
content:
*/*:
schema: {}
..but responses isn't a required part of an openapi specification, so you can just omit it entirely if you like. You would only need to do the above if you want to be more specific for error responses (e.g. 4xx) and leave the 2xx response as more lax.
Given the questions in this thread, we might need to be more explicit in the specification that absence of a specification does NOT indicate that a particular component must be absent; rather the lack of a specification for a thing means that anything is allowed. I'm sure there is some kind of strict language in RFC-style that can be used here, rather than doing it colloquially.
Thanks for the detailed response!
- Do you mean "There must not be a Content-Type response header"? If so, you can specify that, by defining a header with a false schema.
- Do you mean "There must not be a Content-Length response header"? If so, see above.
So something like:
responses:
200:
description: "Everything went OK!"
content:
*/*:
schema: false
Right?
Is an exception like "A 204 response without schema can be interpreted as a false schema" viable? I know this is ugly, but I saw multiple APIs do:
responses:
204:
description: "Don't expect any content from me lol. I'm a 204"
- Do you mean "Okay, there could be a Content-Length header, but if it exists, it must be zero"? If so, you can specify that too, with
"required": falseand"schema": "const": "0".
You mean that as a schema for the Content-Length header, right?
Given the questions in this thread, we might need to be more explicit in the specification that absence of a specification does NOT indicate that a particular component must be absent; rather the lack of a specification for a thing means that anything is allowed. I'm sure there is some kind of strict language in RFC-style that can be used here, rather than doing it colloquially.
Maybe something like:
4.8.17.1 Fixed Fields
Field Name Type Description content Map[ string, Media Type Object]A map containing descriptions of potential response payloads. The key is a media type or media type range and the value describes it. For responses that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/*. For responses that match no key a Media Type Object with a true schema SHOULD be applied.
If we want a 204 exception:
4.8.17.1 Fixed Fields
Field Name Type Description content Map[ string, Media Type Object]A map containing descriptions of potential response payloads. The key is a media type or media type range and the value describes it. For responses that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/*. For responses that match no key a default Media Type Object SHOULD be applied, its schema depends on the status code of the response: falsefor 204 andtruefor any other.
The biggest issue with the exception is that it breaks reusability of responses via de $ref mechanism (because now the interpretation of a Response Object depends on the Operation Object that embeds it. Alternatively we can use that the exception is a MAY thing where implementations are allowed but not required to interpret 204 responses in a more strict fashion.
I'm using this specification for such cases:
'204':
description: Success with an empty body
content: {}
It works for openapi-backend by validateResponse check based on this condition: https://github.com/openapistack/openapi-backend/blob/5.11.1/src/validation.ts#L766
I'm using this specification for such cases: ...
That's not what that means, though. What you are saying by using an empty json object for the media types is simply "the response is unspecified", not "the response must be empty". If your tooling is interpreting that differently, that is an error.
@karenetheridge Thanks for the update, that's interesting 🤔
For an empty json object it should be application/json section I guess, like that:
responses:
'204':
description: Success with an empty body
content:
application/json: {}
Anyway,
although the content: {} approach is not an empty JSON object specification, I think you might be right and you got my point correctly: my main intention is to have explicit documentation for the response that must be empty.
I could agree that the content: {} approach is a workaround and it might be an issue of openapi-backend package in particular, however, I'm NOT considering the specification without context as a correct format for a response that must be empty, as described in officiation swagger documentation:
Source: https://swagger.io/docs/specification/v3_0/describing-responses/
Because in that case, technically the body in the response may contain any information without being validated against the specification.
As an alternative, this version is also works for me:
responses:
'204':
description: Success with an empty body
content:
application/json: {}
schema:
type: 'null'
but in that case, I have to put ctx.body = null in endpoint handler:
const endpointHanlder = async (ctx) => {
ctx.status = 204
+ ctx.body = null
}
otherwise, it would be an error:
"message": "Response validation failed.",
"details": [
{
"instancePath": "",
"schemaPath": "#/type",
"keyword": "type",
"params": {
"type": "null"
},
"message": "must be null"
}
],
For an empty json object it should be application/json section I guess, like that:
I'm afraid that's not correct either -- the value under a media type name ("application/json") is a json schema, and an empty json schema ({}) means "no restrictions: anything is valid".
This would indicate the expectation that there is no body (it is saying: for any media type, the body content must match the literal empty string):
...
content:
*/*:
schema:
const: ''
If your particular web framework uses a null value to indicate an empty message body, you could use type: 'null' there, but I expect that most frameworks would always provide the body as a string (although the spec does not yet state that expectation).
You could also do this, which says "if the Content-Length header exists, it must consist of the literal zero value":
...
parameters:
- in: header
name: Content-Length
required: false
schema:
const: '0'
@karenetheridge The parameters option you suggest would be for a zero-length request body, but not for the response body. You can set that header in a Response Object:
"201":
description: Created
headers:
Content-Length:
schema:
const: 0
I think this would indicate a 201 response MUST NOT have content. The Content-Length header could be left off (since I did not set required: true), but if present it must be 0. You could set required: true on it, but some frameworks might not send it- RFC9110 is a little squirrely about that requirement.
the value under a media type name ("application/json") is a json schema
BTW I think you meant Media Type Object here, which is what you show in your example.
oh, yes, at some point I forgot we were talking about responses, which use header as an object, not requests, which use parameters as an array. Sorry for the confusion. Otherwise, I think you just repeated what I already said.
(Unifying these into the same type of object would be a lovely thing to do for moonwalk.)
@karenetheridge yup, I was just re-formatting it for a response. And we will definitely do something about that by 4.0.
Closing in favor of OAI/learn.openapis.org#132 due to concerns discussed in #4611.