OAI / OpenAPI-Specification

The OpenAPI Specification Repository
https://openapis.org
Apache License 2.0
28.93k stars 9.06k forks source link

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

Closed jahlborn closed 7 years ago

jahlborn commented 10 years ago

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.

DavidBiesack commented 9 years ago

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

adamgo commented 9 years ago

:+1: For different media-types support

greenreign commented 9 years ago

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.

logicbomb commented 9 years ago

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.

jahlborn commented 9 years ago

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

Helmsdown commented 9 years ago

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

dvh commented 9 years ago

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!

sbeaupre commented 9 years ago
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? ;-)

fehguy commented 9 years ago

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

arnm commented 8 years ago

+1

logicbomb commented 8 years ago

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

pdufour commented 8 years ago

+1

ashrafuzzaman commented 8 years ago

:+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.

hjoliveira commented 8 years ago

+1

mparker commented 8 years ago

+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 notifications@github.com wrote:

+1

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

webron commented 8 years ago

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

okigan commented 8 years ago

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

webron commented 8 years ago

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

okigan commented 8 years ago

@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 { 
Rikuoja commented 8 years ago

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

mageddo commented 8 years ago

:+1: For different media-types support too

cjc343 commented 8 years ago

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

webron commented 8 years ago

Parents: #586, #574.

ralfhandl commented 8 years ago

+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

phedoreanu commented 8 years ago

👍 for endpoints treated differently based on the Accept header.

danielgindi commented 8 years ago

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.

BigBlueHat commented 8 years ago

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.

roltean commented 8 years ago

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

zaufi commented 8 years ago

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

sicrossley commented 8 years ago

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

pc-alves commented 7 years ago

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 commented 7 years ago

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

pc-alves commented 7 years ago

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

BigBlueHat commented 7 years ago

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

fehguy commented 7 years ago

https://github.com/OAI/OpenAPI-Specification/pull/761 which was closed via https://github.com/OAI/OpenAPI-Specification/commit/827f76a27994cde6bee8cc16e70457fa2b822725

webron commented 7 years ago

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

baynezy commented 7 years ago

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

ePaul commented 7 years ago

@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 commented 6 years ago

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

MikeRalphson commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

Accept: application/json; prefer=minimal
BigBlueHat commented 6 years ago

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

handrews commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

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

KeithProctor commented 6 years ago

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

handrews commented 6 years ago

@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 commented 6 years ago

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

mataqvazadeh commented 3 years ago

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.