Openapi-specification: Support for multiple request/response models based on headers

Created on 9 Oct 2014  Â·  99Comments  Â·  Source: OAI/OpenAPI-Specification

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.

OpenAPI.Next Proposal Sub Issue

Most helpful comment

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

All 99 comments

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

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.

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.

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

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

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

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.

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

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.

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

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

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.

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

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

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

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

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

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.

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.

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.

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

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.

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

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.

Or alternatively, to make the "schemas" thing consistent, and show off the other location of consumes/produces:

paths:
  /things:
    post:
      operationId: post-things
      consumes:
        json: application/json
        protobuf: application/vnd.google.protobuf
      produces:
        json: application/json
        protobuf: application/vnd.google.protobuf
      parameters:
        - name: data
          in: body
          description: Data
          schemas:
            json:
              type: array
              items:
                type: string
            protobuf:
              title: ThingPostMessageProtobuf
      responses:
        200:
          description: An array of products
          schemas:
            json:
              type: array
              items:
                type: boolean
            protobuf:
              title: ThingResponseMessageProtobuf
  • Give friendly names to the mime types used in consumes/produces
  • Make an alternative to "schema" called "schemas" that allows you to make multiple schemas, keyed by the applicable friendly names.

    • Schemas are chosen by the ContentType and Accept headers where applicable.

Here's another idea that's like my first example but more consistent about things. It allows you to nest all properties of a response under the applicable content type, in case you want to describe that content type. It's unfortunately less backward compatible though.

paths:
  /things:
    post:
      operationId: post-things
      consumes:
        json: application/json
        protobuf: application/vnd.google.protobuf
      produces:
        json: application/json
        protobuf: application/vnd.google.protobuf
      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:
          json:
            description: JSON response
            schema:
              type: array
              items:
                type: boolean
          protobuf:
            description: Protobuf response
            schema:
              title: ThingResponseMessageProtobuf

I just switched to Swagger 1.5M2 and half my documentation disappeared because of this. How is it possible this was missed in the Swagger 2.0 spec? For example, I have a resource to which you can POST JSON, a file in MULTIPART_FORM_DATA, or a raw file in APPLICATION_OCTET_STREAM. So now, not only can't I use Swagger Annotations to document my API, I apparently can't even use Swagger 2.0. Can someone please explain the rationale here, because it escapes me?

@BigSocial - Swagger never supported this paradigm, it was not removed in Swagger 2.0.

Swagger does not attempt to cover all use cases, and we don't hide it. I honestly don't recall right now if this specific feature was requested in the workgroup we had for 2.0, and if it was why specifically it was rejected.

Swagger 2.0 was a huge revamp of the old version(s) and we couldn't possibly contain all features and requests. As you can see here, there's a healthy discussion with suggestions and we'd certainly take it under consideration for the next version of the spec.

@webron - Thanks very much for your quick reply.

First, I'd like to counter your assertion that Swagger never supported this. Using Swagger core 1.3.10 this code...

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;

@Path("/v1/test")
@Produces("text/plain")
@Api(value="test", description = "A test")
public class SomeResource {

   @ApiOperation(value = "Get Text")
   @GET
   public String doGetAsPlainText() {
       return "One";
   }

   @ApiOperation(value = "Get HTML")
   @GET
   @Produces("text/html")
   public String doGetAsHtml() {
       return "<html><body>One</body></html>";
   }
}

...produces this JSON...

{
apiVersion: "1.0.0",
swaggerVersion: "1.2",
basePath: "/api",
resourcePath: "/test",
produces: [
    "text/plain"
],
apis: [
    {
    path: "/v1/test",
    operations: [
        {
        method: "GET",
        summary: "Get Text",
        notes: "",
        type: "void",
        nickname: "doGetAsPlainText",
        parameters: [ ]
        },
        {
        method: "GET",
        summary: "Get HTML",
        notes: "",
        type: "void",
        nickname: "doGetAsHtml",
        produces: [
            "text/html"
        ],
        parameters: [ ]
        }
        ]
    }
    ]
}

...which produces this output from Swagger UI...

shot1

...so you can imagine my surprise when this functionality disappeared.

Second, and more importantly, while I understand there's no way Swagger can be all things to all people, let me refer you to the example code in section 3.1.3 of the Jersey 2.17 User Guide. Section 3 of this document describes what is essentially the heart of JAX-RS. The code below is some of the first a developer new to Jersey might see, and is replicated in any number of blog posts and books on Jersey.

@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
    @GET
    public String doGetAsPlainText() {
        ...
    }

    @GET
    @Produces("text/html")
    public String doGetAsHtml() {
        ...
    }
}

As you see, this simple example early in the documentation for the JAX-RS reference implementation cannot be documented using Swagger 2.0. You may see this as a fringe feature of JAX-RS, but the Jersey team certainly doesn't and (I'd venture to say) nor do the many developers working on APIs that contain something other than the simplest JSON in/JSON out resources. Certainly the code above can be changed to use a single method, but doing so adds code, adds complexity and reduces readability. In my particular case, a single method that accepts JSON, MULTIPART_FORM_DATA, or APPLICATION_OCTET_STREAM would be very ugly if it worked at all, which I find unlikely.

By dropping support for this, Swagger 2.0 has essentially removed developer's ability to use this powerful, very visible and commonly used feature of JAX-RS, or forced them to find an alternate documentation method. I strongly urge you to consider returning support for this excellent, elegant coding paradigm to Swagger.

+1

@BigSocial - what you showed in the 1.3.10 version is a _bug_ - it is definitely not supported by the spec, and wasn't supported in 1.2

From the 1.2 spec - https://github.com/swagger-api/swagger-spec/blob/master/versions/1.2.md#523-operation-object - In the operations array, there MUST be only one Operation Object per method..

So this functionality wasn't dropped, it was never supported. Bug or no bug, it was not part of the spec.

As for JAX-RS allowing that functionality, that's true. Other frameworks allow this functionality as well. However, Swagger does not try or aim to cover 100% of the design options out there. It's an evolving spec and with each version we try to add more functionality, but we definitely don't try to cover it all.

That said, I believe this is a strong candidate for the next version of the spec. I cannot guarantee what will go in, be rejected, or postponed, but it definitely seems many users look for this functionality which to me make it a strong candidate.

We just need to find a proper way to describe it thinking of the ecosystem as a whole. Don't know if we've hit the right spot of it already.

@webron - Not sure if this should make me laugh or cry :smiley:

I appreciate your response. Just a couple points and then I'll leave this be for now.

  1. The fact that it worked and worked perfectly in 1.3 should say something to us about the flexibility of the old spec and web UI code even if unintended.
  2. By moving operations out of an array and into individual objects with fixed names you may have SPECed yourselves into a corner vis-Ă -vis elegantly supporting method overloading, unless you're willing to allow duplicates which is valid in JSON but probably wouldn't work in JavaScript and is certainly not how people think about JSON. IMO the old 'method' field was much more flexible and would make resolving this from the standpoint of the spec much easier. My solution would probably be to solve it at the method level anyway, even if you have to support something less than perfect like "get2", "get3", etc.

UPDATE: Now I think about it you could allow each method to be an object (the normal case) or an array. This would leave the spec backwards compatible but allow arbitrary overloading of any HTTP method.

Here's hoping this make it into the next round of the spec.
Thanks!

FWIW, it's not valid in JSON and any parser that reads that would use either the first or the last definition with the same keyword, overriding the rest.

The array vs. single object approach is interesting, thanks for the suggestion. Feel free to bring in more input if you have it, it's very much appreciated.

I don't want to beat this topic to death. I do want to address the "swagger does not try to aim to cover 100% ..." comment. This has come up a few times, and i fully support that ideal. However, i feel like that response is trying to frame this suggestion as some sort of "fringe" or "non-standard" need. I think that the concept of multiple media types for a resource is a "main stream" REST thing that swagger should clearly support. I was part of the 2.0 spec working group and the only reason i didn't push on this earlier was because i didn't realize it until too late (my company didn't actually try to implement the spec until it was finalized).

It's not about being fringe or non-standard. Nobody really pushed for this requirement in the 2.0 work group, and as you say, you were there to witness that. Had people pushed for that more in the work group or prior to that, it would have probably gotten it. We can't possibly predict the requirements of all users, and this is exactly why we opened up the discussion in the work group. This is also why we created a dedicated repository for the spec to allow people to be more involved and affect the spec.

I can assure you that some requests are not going to make it into the spec, just like I can assure you that many others will. I've said before that that I believe this request is a strong candidate (among others). The gap is obvious and we need to fill it. I don't think there's an agreement on the structure though, and that's what we should concentrate on.

For any feature to be considered, I'd like to see a few things:

  • A clear definition of the problem.
  • References to public APIs that will encounter this problem when documenting.
  • Support from additional users.
  • A proposal for a representation that can solve the problem.

I'm definitely not saying _all_ of the above is required for a proposal to be accepted, just that it can really help in the process.

The proposal is probably the most difficult part. It needs to be elegant (that is, easy to be written manually as well and not look like a blob of information), be reasonably easily supported by the tools of the ecosystem (editor, ui, codegen and of course other tools), and be able to supported by the 'major' development languages/frameworks out there. Try not to hold me to that definition and get the general idea here.

For this issue, I would love to see clearer suggestions for implementation. There have been a couple of suggestions but again, no feedback from most users in this thread (and out of it).

I really appreciate the contribution of everyone here and believe we can find the proper solution. It's definitely not impossible to do.

Are we really going to continue back and forth on this? I didn't say it's a requirement for a proposal to be accepted. I said I'd _like to see_ these things. And right after the bullets, I specifically said they are definitely not all required. There can even be one-line proposals that would be accepted.

And this _is_ the spec community.

Sorry, my last comment wasn't super constructive. i just feel like there's been a lot of pushback on this feature request when it seems like a pretty straightforward need for REST APIs. your bullet point comment seemed to imply that the chances of acceptance are slim unless you meet all those criteria.

for your comments on implementation, you desire something which fits will with the current swagger ecosystem. frankly, i only use swagger from the perspective of generating it for our API server. My primary desire is to be able to describe the functionality that our server provides. i don't know much about how the rest of the swagger ecosystem works, or what would "work best". i would imagine that you have a much better handle on that. there are a number of proposals in this thread so far, which one looks the best to you?

On the contrary, @jahlborn. The reason I push for it is because I believe this is one of the stronger candidates for the next version and would rather be able to present it as a ready-and-packaged solution instead of opening it for a long discussion. It's better if we can use the time prior to the official process so we can spend the official process to go through the possibly more controversial issues (keep in mind I'm in no position to guarantee anything regarding this feature request).

Completely understand your point regarding the ecosystem, and obviously not all users can see the whole effect, which is absolutely fine. I admit that personally I didn't dive into the suggestions that much and will set aside time to go over the stronger feature requests and provide my own input. For now, I just try to get the community involved and get feedback.

Thanks, @webron - I appreciate being open to addressing this issue in the next rev of Swagger.

My primary use case is overloading a method+path with different Content-Type or Accept header media types (body type parameter). Thus I would like:

  1. associate media types with schemas
  2. a means to "merge" overloaded methods into one in the UI. For example, I really don't want the UI presentation that @BigSocial posted above in comment 96417650 above
    that is I don't want
    GET /v1/test Get Text
    GET /v1/test Get HTML
    I want just one entry
    GET /v1/test Get resource X
    and use the accept/consumes UI selectors to choose the media types.

This merging does not have to be automatic, but I would like to have some way to link them so they are merged.

Note that this also means that selecting a media type for the request body would also (in the UI) choose a different schema and sample body. This is why I want to associate media types with schema.

That is, there is an _abstract_ API that consists of method+path and there is a _implementation_ that may consist of multiple controllers per method+path. (We use spring MVC which supports multiple controllers for a method+path. The different controllers can specify different consumes= or produces= media types and the Spring dispatcher handles this, calling the right controller based on the value of the Content-Type or Accept headers). I don't want such _implementation decisions_ to affect the _visible_ API presented in the Swagger UI or in static doc (HTML) we create with Swagger codegen. I want the consumer to see the _abstract_ API.

Hope this helps.

@DavidBiesack - Thanks! That's a very important input regarding the UI. As you said, it may be configurable, but very interesting.

:+1: for what @DavidBiesack.

To add to it, I would think documenting _other_ media types is not in scope for swagger, which in itself is one (at least I'd like to push for it to be one) :smiley: The reason I'd say its out of scope, is I don't think the swagger toolchain would be responsible for enforcing and validating these extensions. For e.g. we wouldn't try and describe or validate a html response for the text/html media type.

Now it might be a different thing if we do end up with json dialects masquerading as specialized media types e.g. application/vnd.customer+jsonor application/vnd.order+json etc. and we use json schema to document that; which I'd still argue is an implementation detail. I say that because there is an implicit shared understanding between a client and server when say an operation produces a response with the application/vnd.order+json media type. Media types are in itself self describing for e.g. standard media types like HAL, Collection+Json, Siren etc. and I don't think swagger specification should try to address describing a media type.

@DavidBiesack I agree that the ultimate UI product should have as much or as little merging as necessary in order to create the best user experience. What I'm against is having the core/spec try to generate/describe this merge. Many of spec change suggestions above solve point use cases but don't address the fact that trying to coerce all variations and combinations of produces, consumes, parameters, etc. into the existing Operation Object will likely generate a combinatorial explosion, will certainly be ugly, and, I'm guessing, struggle to maintain backwards compatibility with the 2.0 spec.

My recommendation is to allow the fields in the Path Item Object that currently take Operation Object (get, put, etc.) to also allow an array of Operation Objects. I'll defend this notion with the following points.

  1. It's an extremely easy to understand extension of the 2.0 spec.
  2. It's structurally one for one with JAX-RS annotated methods and would lose none of their semantic clarity.
  3. It solves a huge swath of the use cases (not formalized yet, sorry @webron) we've been discussing.
  4. Being so high level and simple it's likely to be easier to implement for developers than if each produces, consumes, parameter, etc. for a single Operation Object has to be annotated with which of the other produces, consumes, parameter, etc. it applies to. In my experience, changing high level, important constructs (Operation get becomes List<Operation> get) is often easier than diving into the weeds in order to add links between low level constructs.
  5. From the Swagger UI's standpoint, I'd venture to guess that the brain dead implementation, such as the one I was using despite it being a bug, would be easy to add. After which they could start thinking about a more merged UI model.
  6. It's 100% backwards compatible with the 2.0 spec.
  7. It solves my personal use case :smile:

Also related is #342

:+1: on considering this for Swagger.Next. Supporting media-types seem like a worthy thing to support.

:+1: on support for different response types in some way, but would need to support based on header or param.

:thumbsup: for this.

We are implementing an API similar to Github where we use the media type as a way to version our API. See the following Spring MVC code for an example

@Controller
@RequestMapping(value = "/api/service")
public class DemoController {

    @ApiOperation(value="Test v1")
    @RequestMapping(value = "/test", produces = "application/vnd.mycompany.v1+json", method = RequestMethod.GET)
    public String getV1() {
        return "v1";
    }

    @ApiOperation(value="Test v2")
    @RequestMapping(value = "/test", produces = {"application/vnd.mycompany+json", "application/vnd.mycompany.v2+json"}, method = RequestMethod.GET)
    public String getV2() {
        return "v2";
    }

}

Different versions could be accessed via the _Accept_ header:

# version 1
curl -v "http://localhost/api/service/test" --header "Accept: application/vnd.mycompany.v1+json,application/vnd.mycompany+json"

# version 2
curl -v "http://localhost/api/service/test" --header "Accept: application/vnd.mycompany.v2+json,application/vnd.mycompany+json"

# latest version
curl -v "http://localhost/api/service/test" --header "Accept: application/vnd.mycompany+json"

:+1:

+1

I'd like to have this as well. This is very useful when I'm implementing some general pass through API where I want to have one method but if user input "plain/text", I return "plain/text" as a string in the response. And if they input "application/json", I return a json object.

Just for reference, I just hit the same problem. ... We have (or will have, we are currently in the pre-design phase) an API which can produce for the same conceptual thing (resource) both a PDF and a JSON representation. It looks like I can't specify this with Swagger, as long as both have the same URL.

Our company's API guidelines also say that versioning (if incompatibilities can't be avoided) should be done by Accept header with new media types for new versions, which means we can't use one Swagger document to document both versions. (The same guidelines also tell me I have to use Swagger. :-/ )

About the syntax, I like the "schemas" proposal from mohsen1, though that needs to specify the content-types for each schema somehow. It seems like the second proposal from nickretallack has something here. (I'm not sure these aliases ("friendly names") are needed, maybe simply using the media types themselves as map keys would be enough.)

I've been thinking about this problem for a while....

My first preference is to be able to use an array under a path as suggested above by BigSocial on April 27 2015 but that cannot happen until a new Swagger spec is released.

In the meantime, a workaround that I am experimenting with is to use a marker suffix in the paths. It exists only to create an artificially separate path in the Swagger spec, but one which is not really in the API.

Thus, if I want two different POST methods on an endpoint, I could use the following Swagger

 "paths" : [
    "/models/{modelId}/elements#1" : {
        "post" : { ... }
    "/models/{modelId}/elements#2" : {
        "post" : { ... }
    ]

These #1 and #2 suffixes would be stripped out when Swagger UI or Swagger Editor Try It out! makes the API call, stripped out in swagger-codegen, etc.

They are 'transient' artifacts just to allow Swagger to describe each abstract API call separately. Swagger UI would call

  POST /models/{modelId}/elements

in both cases.

This would allow complete segregation of not only the method but also parameters such as headers, consumes/produces, schema, etc. Thus, if a back end implementation supports the desired overloading, the separate controllers emitted by swagger-codegen would work.

(Some other syntax such as $$1 or {$1} could be used to avoid ambiguity with # in URLs referring to anchors. The syntax is not important, it is the marker or discriminator in the path that is important).

I think this addresses this issue as well as Open Support an operation to have multiple specifications per path (e.g. multiple POST operation per path) #182

:+1: For different media-types support

Just ran into this as well. I'm going to append some garbage regexp onto the end of one of my paths to get it to display my second POST and consider changing the path completely if this isn't picked up by swagger.next.

Question - will this be extended to query string parameters as well? A number of APIs (including mine) serve different resources based on qs params. I see a number of related issues (https://github.com/swagger-api/swagger-spec/issues/56, https://github.com/swagger-api/swagger-spec/issues/123, https://github.com/swagger-api/swagger-spec/issues/164) but none of them address multiple response objects per query param directly.

@logicbomb - I purposely avoided that in this issue because i knew that it was frowned upon in the original swagger v2 discussions (i menionted that in one of my early comments). And i'm pretty sure that the other issues, e.g. #164 , would include having different response objects based on the query params.

+1 very interested in this getting added in next version. Would love to be able to do GET:/v1/things/{id} and specify that application/json returns #/definitions/thing and application/pdf returns a file.

It's definitely a must-have to support different responses based on content-negotiation. The most important reason for this in my opinion is JSON-LD. The semantic web is finally taking off and if you want Siri, Cortana or Google Now to 'understand' your data, you should start providing JSON-LD, telling machines what the data is about and providing links to external web pages as identifiers so every 'thing' knows what we're talking about.

This means my application/json response would look like this:

{
    name: "Ferrari F50",
    brand: "Ferrari"
}

While my application/ld+json response would look like this:

{
  @type: "http://schema.org/Car",
  name: "Ferrari F50",
  brand: {
      @id: "http://dbpedia.org/resource/Ferrari",
      @type: "http://schema.org/Brand",
      label: "Ferrari"
  }
}

Another use case I can imagine is the use of Hypermedia in APIs. Perhaps you want to support both JSON-API (application/vnd.api+json) as HAL (application/hal+json) but both rely on completely different response structures.

RAML supports this by simply adding the mime type to an example response, instead of working with models. Perhaps this is something to take a look at!

What is the progress on this?

For clarity, I want to stress out that it is really needed to version data contracts, not resources. Resources pretty much stay the same for a very long time, but data contracts can change more frequently.

Multiple schema entries

I think there was a very good proposal made by @jahlborn in this comment.

This is also similar to the way it is done in RAML 0.8 (cfr. 'Schemas' paragraph) and this works quite well in practice.

What I would enforce is that the media-types must also be 1 tot 1 defined in the "produces" entry, if not, it is an error. (don't see added value in friendly names as proposed above, it makes it too verbose)

Multiple in:body entries

I also again agree with @jahlborn for the post/put solution with multiple in:body entries he proposed, again with the enforcement of the fact that the mime-types must be present in the "consumes"entry for this case.

My 2 cents

I really think it should be limited to these 2 cases, because all we are talking about is the data contract (ie. mime-type set in accept or content-type headers) for the retreived or uploaded 'body'.

Question now to @fehguy is: when can this be added to the spec or what is holding it back? ;-)

Yes this is a frequently requested change and you've summed up the options well. It will break all tooling though and require a major version change. There are other pending requests that is like to see considered for the next version as well. We are getting closer to the candidate list, again the challenge will be breaking tooling across the board, and that is best mitigated by some back compatibility planning. There are over 1500 swagger tooling projects now so it is a big task...

+1

:+1: content negotiation seems to be a fundamental part of a RESTful API, you all really should try to find a way to support it

+1

:+1:

JSON and ATOM might not be in same structure. We have been supporting JSON for sometime and might want to add ATOM later in an existing API, which most likely will be in a different structure.

+1

+1, especially now that the specification is separate from the
implementation, and should no longer inherit the shortcoming which existed
in the implementation

On Mon, Jan 25, 2016 at 10:14 AM, hjoliveira [email protected]
wrote:

+1

—
Reply to this email directly or view it on GitHub
https://github.com/OAI/OpenAPI-Specification/issues/146#issuecomment-174589818
.

@mparker - fwiw, this was never 'not added' to the spec because of the implementation. It was 'not added' because nobody asked for it when the current version was being discussed (believe it or not). Unfortunately, this ticket was opened shortly after the version was finalized and we couldn't just go and change it (like for other requests as well). While can't guarantee, I seriously doubt this will not make it to the next release (regardless of implementations).

@webron many may have assumed that it is or would be there, as Accept/Content-Type header are essential in HTTP protocol. Here is an example how java's ws rs as exposes it :

@Produces({MediaType.APPLICATION_JSON, ResourceConsts.APPLICATION_YAML})
@Consumes({MediaType.APPLICATION_JSON, ResourceConsts.APPLICATION_YAML})
 public interface MyResource { 

The specification of accept/response content type is very naturally specified and list is extended/modified over time.

@okigan - _that_ is supported today in the spec. What's not supported is saying if application/json return structure X and if application/YAML return structure Y. Another take is support output versioning with the mimetype as the OP points out, that's also not supported. Not sure if you were part of the work group around 2.0 (which was public) - we discussed the smallest of the details. Unfortunately we had some misses, and hopefully the next version will mend the major misses and add some new and exciting features.

@webron I guess I assumed, one could add a vendor specific type to that list -- which could be a different schema:

@Produces({"application/vnd.mycompany.type", MediaType.APPLICATION_JSON, ResourceConsts.APPLICATION_YAML})
@Consumes({MediaType.APPLICATION_JSON, ResourceConsts.APPLICATION_YAML})
 public interface MyResource { 

+1 here, the City of Helsinki APIs cannot be fully specified ATM.

:+1: For different media-types support too

+1 - got an endpoint treated differently based on the Accept header and can't properly document.

Parents: #586, #574.

+1: we respond with JSON or Atom (XML) depending on the Accept header provided by the client. And allow JSON or Atom (XML) request bodies with POST, PATCH, and PUT with corresponding Content-Type header

👍 for endpoints treated differently based on the Accept header.

Some APIs support accepting different models in the POST body, based on whether the body is an Object or an Array.

When generating code, we can easily add an if statement to check if the body is an Object or an Array. It should validate that the input type matches the model anyway. So this level of model differentiation is very easy to implement.

Negotiating on Accept and Content-Type is a great place to start. However, it would be ideal if this (or something like it) could support the other options for negotiation: Prefer header, Accept-Encoding, Accept-Language, etc.

Perhaps basing the handling on HTTP's Vary header would feel familiar.

Any updates on this? When will this be implemented and released? We really need this fix.
Thanks!

Meanwhile, trying to express JFrog Artifactory REST API, I've faced w/ this issue at the very beginning of my learning of OpenAPI Specification.
Here is a real life example:

both have the same URI patterns, but different MIME type of results (which is supposed to be used to distinct them). And there is no way to express that nowadays :(

:+1: I have exactly the use case with different schemas for different media types on the same path.

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.

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

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

@fehguy URL or it didn't happen. :stuck_out_tongue_winking_eye:

Responses are now specified based on media type! :tada:

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

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

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

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:

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.

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

Accept: application/json; prefer=minimal

@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:
https://github.com/OAI/OpenAPI-Specification/issues/146#issuecomment-235136624

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

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

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
}

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

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

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

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rossi-jeff picture rossi-jeff  Â·  5Comments

Prasanthmv picture Prasanthmv  Â·  4Comments

ricellis picture ricellis  Â·  3Comments

andy-maier picture andy-maier  Â·  4Comments

rocchisanijl picture rocchisanijl  Â·  5Comments