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

Support for multiple request/response models based on headers #146

Closed
jahlborn opened this issue Oct 9, 2014 · 100 comments
Closed

Support for multiple request/response models based on headers #146

jahlborn opened this issue Oct 9, 2014 · 100 comments

Comments

@jahlborn
Copy link

jahlborn commented Oct 9, 2014

I guess this is a 2.0+ request as 2.0 is already "released".

Basically, there is currently no way to express a single path which may have different request/response formats based on additional criteria, namely headers. two possible scenarios:

  1. An API which supports both xml and json, but the models for each format are different enough that they cannot be expressed via a single json schema w/ xml annotations (even if the xml format is expressible using the xml annotations).
  2. An API which supports versioning the representation via the media-type. e.g. application/my+v1 returns a json model which is sufficiently different from the application/my+v2 model such that i cannot describe them both using one unified model.

If supporting different responses for arbitrary headers is too big of a change, just supporting multiple media-types (for request and response) would be sufficient for the cases described here.

@mohsen1
Copy link
Contributor

mohsen1 commented Oct 13, 2014

Let's say you have your content type param as a path param. Like what Reddit is doing. See this. Then it will be represented like this:

swagger: 2
info:
  version: "1.0.0"
  title: reddit
paths:
  /r/{sub}:
    get:
      responses:
        200:
          description:  HTML
    post:
      responses:
        500:
          description: You can't
  /r/{sub}.json:
    get:
      responses:
        200:
          description:  JSON

Now imagine the same API wants to use content-header for the response type. We can have another level of abstraction for different content types

swagger: 2
info:
  version: "1.0.0"
  title: reddit
paths:
  /r/{sub}:
    get:
      content-types:
        XML:
          paramaeters:
            -
              name: type
              in: header
              description: Content-Type
          responses:
            200:
              description:  HTML
        JSON:
          paramaeters:
            -
              name: type
              in: header
              description: Content-Type
          responses:
            200:
              description:  JSON
    post:
      responses:
        500:
          description: You can't

This is just a raw idea. I don't know if it's even practical

@webron
Copy link
Member

webron commented Oct 14, 2014

The first sample @mohsen1 presented is something that can be done today as the content type is being controlled by the path and there's no issue representing it.

The second sample is closer to what @jahlborn expected, I assume.

I'm not convinced how common this use case is (not saying it doesn't exist though, of course). I'd hope to see a solution that doesn't complicate the spec for users who don't need this feature to avoid complication as much as possible. We're trying to create a spec that's simple enough for people to use without having to deal with complexity if they don't need it. I suspect we'll manage to find an elegant solution for this one though.

@jahlborn
Copy link
Author

If we were only looking to support different schemas based on media-type, then the current spec isn't very far off. you can already provide a list of media-types in produces and consumes. if you allowed multiple "in:body" request parameters with an additional media-type parameter, then that would cover the requests. the harder part is the responses. ideally, you would want "schema" to instead be "schemas", which is a map from media-type to the Schema Object. This overall design assumes that everything else about the operation is the same other than different formats based on media types. the second example from @mohsen1 is a much bigger change which allows much greater differences in the operation for each media type.

@mohsen1
Copy link
Contributor

mohsen1 commented Oct 14, 2014

I like the schemas idea. That way if an endpoint returning different schemas based on any parameters we can describe it. This is how it's going to look like. Note that there is no mapping from the parameter to the schemas. Is that a problem?

swagger: 2
info:
  version: "1.0.0"
  title: reddit
paths:
  /r/{sub}:
    get:
      parameters:
        - name: Content-Type
          in: header

      responses:
        200:
          description:  HTML or JSON based content header
          schemas: 
            -
              type: string
              description: XML String
            -
              type: object
              propertirs:
                name: 
                  type: string
    post:
      responses:
        500:
          description: You can't

@fehguy
Copy link
Contributor

fehguy commented Oct 14, 2014

per design, we don't overload response type definitions for the same response code.

@jahlborn
Copy link
Author

@fehguy - i understand that this is currently the case, which is why this issue is tagged as "swagger.next". or are you saying this will never be the case, in which case this issue is moot?

@fehguy
Copy link
Contributor

fehguy commented Oct 14, 2014

sorry, carry on. I suggest proposing anything with that label--there was a fair amount of discussion in the workgroup about avoiding overloading response types.

@jahlborn
Copy link
Author

@fehguy - so going back and reviewing that thread, i agree with most of the arguments against overloading in that thread. specifically, you said "Yes, swagger has always had a deterministic response type per operation (combination of http request method + path + http response code)". later you mentioned the proposal to include all the query params, and that you felt that was "ugly". i largely agree with this. while my initial description above does indicate choosing model based on any header, that probably falls into a similar category as all the query parameters. I would agree that the REST ideals aim for the url and http method defining the resource. however, i would argue that the media type is still an important factor. a given resource can have many formats, and that is controlled by the media-type related headers. So, i still think there should be (at least) the ability to specify multiple models for input and output based on media-types.

@DavidBiesack
Copy link

For another data point, I will mention that our APIs currently allow overloading of path/methods and use the Content-Type and Accept headers to indicate the representation of the request and response bodies. Thus, a GET or PUT or POST to /a/b/c may allow any of n different, discrete media types (each are different representation of the resource). In particular, we use different media types to distinguish different JSON bodies, i.e.

GET /a/b/c 
Content-Typet:application/vnd.sas.myresource+json 

GET /a/b/c 
Content-Typet:application/vnd.sas.alternate+json 

GET /a/b/c 
Accept:application/pdf 

POST /a/b/c 
Accept:text/html

Of note, we do not rely on just a "file extension" such as .json to determine a response type,
as we allow for multiple different application/vnd.sas.*+json media types for some resources.

So this ability (and the association in swagger-ui of a media type with a schema)
is very important to our potential use of Swagger.

@mohsen1
Copy link
Contributor

mohsen1 commented Oct 20, 2014

This feature certainly is going to be part of next version of Swagger. There is no way to not break the standard and have operation overloading. I have another proposal for operation overloading syntax. Here it is:

swagger: '2.1'
info:
  version: "1.0.0"
  title: File
paths:
  /file:
    get:
      responses:
        200:
          description:  Return HTML by default
    get[application/vnd.sas.a+json]:
      responses:
        200:
          description:  Return JSON
    get[application/pdf]:
      response:
        200:
          description: Returns PDF

Advantages:

  • Method overloading which is not breaking JSON and YAML semantics
  • treats operations that act differently based on Content-Type as different operation

Disadvantages:

  • This is only limited to Content-Type header, operation overloading can happen with any type of parameter like a query parameter (Reddit for example).

Them main conflict is that we are trying to model an API in a tree structure while operations and parameters can not have parent/child or sibling relationships

@jahlborn
Copy link
Author

@mohsen1 from the comments made by @fehguy, i'm guessing that this would probably only ever be entertained for media-types, not for headers in general. i'm pretty much okay with that.

i think that putting the media-type with the method, though, opens up too much freedom for abusing the media-type. the current design of swagger works from the standpoint that a path and http method uniquely define a resource. by overloading at the http method level, you are allowing all the parameters, responses, etc. to be different based on the media-type. this would essentially make the argument that a resource is defined by path, http method, and media-type.

in contrast, my compromise proposal at this point, is that path and http method still uniquely define a resource, but that we should allow for different representations of that resource based on media-type (this is essentially what @DavidBiesack is asking for, and basically what the examples i gave in the original description describe). as i said above, i think that using a schemas property (both in responses and in the body parameter) would be a relatively small change from the current swagger spec which would allow for different representations of the same resource, but otherwise keep the rest of the resource's spec unchanged.

swagger: '2.1'
info:
  version: "1.0.0"
  title: File
paths:
  /file:
    get:
      responses:
        200:
          description:  Return HTML by default, also support JSON and PDF.
          schemas:
            default:
              ... html definition ...
            application/vnd.sas.a+json:
              ... json definition ...
            application/pdf:
              ... pdf definition ...

@maxlinc
Copy link
Contributor

maxlinc commented Oct 27, 2014

path and http method still uniquely define a resource, but that we should allow for different representations of that resource based on media-type

Definitely agree and think that overloaded schemas looks like a much easier spec change to deal with than overloaded methods.

@DavidBiesack
Copy link

However, with POST actions especially, there can be several unrelated operations that would be better presented to the user as separate POST actions in swagger-ui because they do very different things to the same resource. Each of these different operations should have a unique description string and unique media types to select from and other unique parameters (such as query parameters), and thus have separate presentation in swagger-ui. By simply allowing only just schema overloading, there is no way for me to constrain query parameter A to be used with just media type X, and query parameter B to be used with media type Y. These are implemented as two independent operations and it would be nice to be able to model them independently in Swagger.

So I think a proposal i saw elsewhere to annotate the operations themselves (to provide unique keys in the JSON object) would actually be more flexible than other workarounds to the fact that Swagger only allows one POST per path when a REST API may in fact support many independent POST calls.

i.e. something like

paths:
 /a/b/c:
   get: ....
   put: ...
   post.optimize: spec for the optimize operation
   post.merge: spec for the merge operation

@webron
Copy link
Member

webron commented Oct 28, 2014

@DavidBiesack - what you're talking about is different than intended by the original topic, and I'd recommend opening a separate thread for it. They may be solved in a similar manner, but providing support for one issue does not guarantee we'll provide support for another.

@webron
Copy link
Member

webron commented Oct 28, 2014

@jahlborn - Do you have references to publicly available APIs that use the method of different mime types for different data representations? Of course, I'm not talking about the basic json vs xml but rather the one you were talking about.

@DavidBiesack
Copy link

@jahlborn
GitHub's REST API lets you GET an issue in multiple formats; see GET /repos/:owner/:repo/issues/:number

and supports these representations of a GitHub issue:

  • application/vnd.github.VERSION.raw+json
  • application/vnd.github.VERSION.text+json
  • application/vnd.github.VERSION.html+json
  • application/vnd.github.VERSION.full+json

See GitHub Media Types :
Custom media types are used in the API to let consumers choose the format of the data they wish to receive. This is done by adding one or more of the following types to the Accept header when you make a request.

@jahlborn
Copy link
Author

Thanks @DavidBiesack, the GitHub api is a great example.

@webron
Copy link
Member

webron commented Oct 28, 2014

That example shows the versioning representation, but not the difference in modeling.
Also one example, is not necessarily enough.

I'd love to see more examples, if you know of any.

The first reason, is to see that it is, in fact, a common practice. The second, is that with more examples we can end up having a definition that covers the use case properly with possible support for some edge cases.

There's no hurry in providing such samples as it'll be a while before this is actually implemented in the spec. Just feel free to point to additional APIs as you encounter them.

@DavidBiesack
Copy link

I disagree; the GitHub API clearly shows the resource representation difference. For a given resource, I can ask for the raw Markdown source; the clean text representation, html representation or full json representation of the same resource. See the Comment Body Properties section where each of these media types is defined.

Also for different representations, the Google Drive API provides an analogous mechanism (though query parameters, not via Accept headers) for the exportLinks. I.e. grab a spreadsheet resource out of Google Drive and it will contain media-type specific export links such as

"exportLinks": {
 "application/pdf”
    : "https://docs.google.com/feeds/download/spreadsheets/Export
       ?key=0AqGUPYh362KvdC1aMURSNVIyVnNYM2ZabVJCRi1GNnc
       &exportFormat=pdf",
 "application/x-vnd.oasis.opendocument.spreadsheet”
 : "https://docs.google.com/feeds/download/spreadsheets/Export
     ?key=0AqGUPYh362KvdC1aMURSNVIyVnNYM2ZabVJCRi1GNnc
      &exportFormat=ods",
 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”
    : "https://docs.google.com/feeds/download/spreadsheets/Export
       ?key=0AqGUPYh362KvdC1aMURSNVIyVnNYM2ZabVJCRi1GNnc
       &exportFormat=xlsx"
   }

geoserver REST API supports multiple media types via Content-Type, for example POST /styles can consume a application/vnd.ogc.sld+xml or a application/json and GET /styles/{s} can return SLD, XML, JSON, or HTML.

@webron
Copy link
Member

webron commented Oct 28, 2014

I'm referring to xml/json interoperability. We're not trying to describe how a response would look differently as a pdf vs xlsx. Github's API doesn't even support XML.

The geoserver documentation is unclear as to how the data formats are actually represented by the different content types.

@DavidBiesack
Copy link

@webron What do you mean by "xml/json interoperability"? Earlier you wrote

Do you have references to publicly available APIs that use the method of different mime types for different data representations? Of course, I'm not talking about the basic json vs xml but rather the one you were talking about.

so I was looking for exactly that - different data representations/mime types of the same resource.

@webron
Copy link
Member

webron commented Oct 28, 2014

I'm referring to point 1 of the original post, @DavidBiesack. @jahlborn knows what I'm talking about as that's basically what started this issue in the first place.

Thank you for looking for samples though, I'm happy to see you're actively trying to pursue it.

@jahlborn
Copy link
Author

Just did a quick google search, found https://xively.com/dev/docs/api/communicating/data_formats/ . their json and xml formats are similar, but i believe there are differences which would fall out of the current swagger support (particularly the response). it looks like they are constrained by an existing "standard" schema for the xml (which also kind of ties into one of my previous requests for first class xml schema support, Issue #54).

@nickretallack
Copy link

Here's what I want:

consumes:
  json: application/json
  protobuf: application/vnd.google.protobuf
produces:
  json: application/json
  protobuf: application/vnd.google.protobuf
paths:
  /things:
    post:
      operationId: post-things
      parameters:
        - name: json
          in: body
          description: JSON payload
          mime: json
          schema:
            type: array
            items:
              type: string
        - name: protobuf
          in: body
          description: Protobuf payload
          mime: protobuf
          schema:
            title: ThingPostMessageProtobuf
      responses:
        200:
          description: OK
          schemas:
            json:
              type: array
              items:
                type: boolean
            protobuf:
              title: ThingResponseMessageProtobuf
  • Give friendly names to the mime types used in consumes/produces
  • Allow multiple different "body" parameters with some property ("mime"?) to differentiate them.
    • This property references the friendly names defined in "consumes".
    • The ContentType header determines which one to use.
  • Allow multiple response schemas, keyed by the friendly names in "produces"
    • The Accept header determines which one to use.
  • The changes to "consumes" and "produces" also apply to their appearances inside of the Path Objects.

@pc-alves
Copy link

Adding my voice to this thread. I am facing this issue of Content-Type dependent POST body schemas.
I am left with two options. Either the API replicates exactly what is in my spec, and I have to find a workaround in the API because the spec does not support defining different schemas; or I have missing information in my spec to be able to implement the behaviour in the API.

@fehguy
Copy link
Contributor

fehguy commented Oct 27, 2016

Schemas which vary by content type has been added to the 3.0 spec

@pc-alves
Copy link

Thanks, @fehguy! Good to know. I'll be following the release of version 3.0

@BigBlueHat
Copy link

@fehguy URL or it didn't happen. 😜

@fehguy
Copy link
Contributor

fehguy commented Oct 27, 2016

#761 which was closed via 827f76a

@webron
Copy link
Member

webron commented Mar 3, 2017

Responses are now specified based on media type! 🎉

@webron webron closed this as completed Mar 3, 2017
@baynezy
Copy link

baynezy commented Mar 16, 2017

Where can I find out more about the release schedule for V3?

@ePaul
Copy link
Contributor

ePaul commented Mar 16, 2017

@baynezy Currently we have a RC0, which is an implementer's draft. I guess we'll have one or some more RCs fixing the currently found issues, and the last one will be promoted to the final release. As far as I know, there is no timeline here – it all depends on the feedback we get from implementers, and the time the contributors find.

@aaronshaf
Copy link

Where can I learn more about media-type-specific requests/responses?

@MikeRalphson
Copy link
Member

Where can I learn more about media-type-specific requests/responses?

The following areas of the specification should be a good start, including the examples which follow each section:

@mpavkovic
Copy link

What about content negotiation depending on request header like Prefer (what @BigBlueHat mentioned in his comment)?

I have a POST API endpoint that returns the same response code and same content type, but actual returned schema varies based on Prefer header. For example, when Prefer is return=minimal only an ID of resource is returned, but when it's return=representation a whole resource is returned.

@baynezy
Copy link

baynezy commented Apr 18, 2018

The schema doesn't really support endless permutations for granularity. You could resolve this on Accept like this:-

Accept: application/json; prefer=minimal

@BigBlueHat
Copy link

BigBlueHat commented Apr 18, 2018

@baynezy well... prefer is a header (re: RFC 7240), not a media type parameter.

Here's an example (though there are certainly others) of using it "in the wild": https://www.w3.org/TR/annotation-protocol/#container-representation-preferences

It was attempting to create an OpenAPI doc for that API that ultimately brought about this comment:
#146 (comment)

So...perhaps this needs a new issue (as this one's closed)?

@handrews
Copy link
Member

@BigBlueHat @mpavkovic I just write the schema such that it works with both the minimal and representation forms. There are several ways you can make the specific alternative more clear. I think the real question is around exactly what you need to convey beyond "this resource understands Prefer" and ensuring that each variation validates.

@KeithProctor
Copy link

KeithProctor commented Aug 30, 2018

I have a related question to this thread but going in the opposite direction. I want to find out if it is possible to create an endpoint that allows a parameter to accept multiple inbound data types? Such as allowing a JSON parameter to be a number as well as a string. This is simply a convenience to our users and would be extremely friend for them.

{
"someParam": "1"
}
or (would be allowed)
{
"someParam": 1
}

@handrews
Copy link
Member

@KeithProctor in OpenAPI 3, {"oneOf": [{"type": "number"}, {"type": "string"}]} should work.
In standard JSON Schema, you can also just do {"type": ["number", "string"]}

@KeithProctor
Copy link

@handrews ... This is for a swagger definition? Right?

@handrews
Copy link
Member

@KeithProctor Is Swagger still on OpenAPI 2? If that's the case then I think you just can't do this in Swagger. OpeanAPI 2 did not support oneOf.

@KeithProctor
Copy link

KeithProctor commented Sep 17, 2018

Sorry I didn't get back to you. This seamed to work in 2.x. The type with list method that is.

@mataqvazadeh
Copy link

We created a rest web service with Nancy in a C# project.
Now we want to use Open API as standard documentation, but we have a problem.
We keep each path version in header of request with special parameter called "GenerationVersion", now I don't know how can I represent multiple version of specific path with specific http method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests