Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do we need to clarify what a response missing content means #3536

Open
philsturgeon opened this issue Jan 31, 2024 · 14 comments
Open

Do we need to clarify what a response missing content means #3536

philsturgeon opened this issue Jan 31, 2024 · 14 comments
Labels
clarification requests to clarify, but not change, part of the spec media and encoding Issues regarding media type support and how to encode data (outside of query/path params)

Comments

@philsturgeon
Copy link
Contributor

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?

@handrews handrews added the media and encoding Issues regarding media type support and how to encode data (outside of query/path params) label Jan 31, 2024
@handrews
Copy link
Member

handrews commented Jan 31, 2024

@handrews handrews added the clarification requests to clarify, but not change, part of the spec label Jan 31, 2024
@iglosiggio
Copy link

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).

@karenetheridge
Copy link
Member

karenetheridge commented Jul 25, 2024

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": false and "schema": "const": "0".

@lornajane
Copy link
Contributor

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.

@karenetheridge
Copy link
Member

karenetheridge commented Jul 25, 2024

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.

@karenetheridge
Copy link
Member

karenetheridge commented Jul 25, 2024

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.

@iglosiggio
Copy link

iglosiggio commented Jul 26, 2024

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": false and "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: false for 204 and true for 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.

@Mifrill
Copy link

Mifrill commented Jan 17, 2025

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

@karenetheridge
Copy link
Member

karenetheridge commented Jan 30, 2025

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.

@Mifrill
Copy link

Mifrill commented Jan 30, 2025

@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:

Image

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"
  }
],

@karenetheridge
Copy link
Member

karenetheridge commented Jan 30, 2025

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'

@handrews
Copy link
Member

@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.

@karenetheridge
Copy link
Member

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.)

@handrews
Copy link
Member

@karenetheridge yup, I was just re-formatting it for a response. And we will definitely do something about that by 4.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clarification requests to clarify, but not change, part of the spec media and encoding Issues regarding media type support and how to encode data (outside of query/path params)
Projects
None yet
Development

No branches or pull requests

6 participants