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 setting accepted media-type on Preload & Fields pushes #46

Open
andrerom opened this issue Mar 13, 2020 · 6 comments · Fixed by #54
Open

Support setting accepted media-type on Preload & Fields pushes #46

andrerom opened this issue Mar 13, 2020 · 6 comments · Fixed by #54

Comments

@andrerom
Copy link
Contributor

andrerom commented Mar 13, 2020

I choose to view Vulcain as a possible REST maturity models level 4. So I'm wondering if it should be possible to specify accepted media-type on pushes / preloads via Preload and Fields in order to fit well with already mature REST API's that uses media type for content negotiation.

In all transparency we use that in eZ Platform, and it's a rather powerful concept as it allows integrators to extend certain api endpoints. But unlike graphQL where caller can ask for whatever, in this case someone would need to code alternative representations.

While it would have to be in a format that can work both in the header and query format, something like the following might be a way to do it:

GET /books/ HTTP/2
Preload: /member:application%2Fvnd.acme.externalmember+json/*/author
Fields: /author/familyName

Open questions:

  • Handling of /, here shown encoded using %2F as possible measure to avoid conflicts with path
  • If type should have to be repeated on Fields: or not if already specified. Here suggested to be optional however no strong opinion on that.
  • Separator to use to show start of media type, here shows as :, however that is legal in URI's so maybe it will have to be something else, did not find any MIME's with this tough
  • Should it also support MIME type parameter? Like ;charset=UTF-8
@dunglas
Copy link
Owner

dunglas commented Apr 3, 2020

Hi @andrerom, Interesting topic!

First, let me explain the current rationale regarding content negotiation:

  • the HTTP/2 Server Push mechanism works like this: the server creates by itself a request, and then push to the client both the request (it generated) and the response
  • when creating the implicit requests (the pushes), the Hub just copy all request HTTP that have been sent by the client for the explicit request

It means that content negotiation is possible, and actually works, but with one assumed constraint: the Accept header will be the same for all requests (the explicit and the implicit). However, the Content-Type of every response can differ (even if it's not usual I guess).

In most cases, using the priority mechanism of the Accept header should be good enough. Ex: I want XML for API responses, and PNG for related images: Accept: text/xml;q=0.9, image/png;q=0.8, */*;q=0.7.

So my first question is: isn't the current capabilities good enough? They are just what HTTP allows.

So about your proposal:

Separator to use to show start of media type, here shows as :, however that is legal in URI's so maybe it will have to be something else, did not find any MIME's with this tough

: is definitely not a possible option because it's the characters used in XML, JSON-LD and all related W3C formats to separate the namespace and the property in compact IRIs. For instance, {"hydra:member": [...]} is used everywhere in API Platform for instance, Schema.org's key are usually schema:prop (ex: schema:Person) regardless of the format etc.

What we can do however, is to add a new escape character in the extended JSON Pointer format as we've done for *: https://github.com/dunglas/vulcain/blob/master/spec/vulcain.md#extended-json-pointer (/ and ~ are already a special char in the original JSON Pointer spec).
We could say for instance that - the special character we choose - must be escaped by ~2).

Finally, is there any example in the RFC corpus of marking a key with a MIME type like you propose? If yes, we should use the same character than the existing one (RFC tries to be consistent altogether). If no, maybe that we shouldn't add this in the main spec (The Vulcain spec is designed to be as minimal as possible), but could be provided as an extension (using the escaping trick I suggest just above)?

@andrerom
Copy link
Contributor Author

andrerom commented Apr 13, 2020

Finally, is there any example in the RFC corpus of marking a key with a MIME type like you propose? If yes, we should use the same character than the existing one (RFC tries to be consistent altogether).

I have looked for it, I tried to look for examples on JSON API / Open REST / RFCs, but didn't quite find what I was looking for, so I think no.

As for Accept header, for some cases that could work. But it's a global header, and Preload and Fields are essentially loading other documents then the main request, which in our case might have different or conflicting* media types

* conflicting as in several of them might be same entity type, root and leaf might be same, but you might want different representation of them due to the data you need. I know this sounds very abstract, so if you want I can try to write a more concrete example on the use case, but TL;DR; both will be "Content" in our case.

@dunglas
Copy link
Owner

dunglas commented Apr 13, 2020

What do you think about an extension (that can be stored in this repo but that will not be proposed to IETF)?

@andrerom
Copy link
Contributor Author

andrerom commented Apr 14, 2020

maybe

But I did kind of find a spec that allows this now, however it's not embedded in the URI itself. See Link header spec for type usage:

 Link           = "Link" ":" #link-value
  link-value     = "<" URI-Reference ">" *( ";" link-param )
  link-param     = ( ( "rel" "=" relation-types )
                 | ( "anchor" "=" <"> URI-Reference <"> )
                 | ( "rev" "=" relation-types )
                 | ( "hreflang" "=" Language-Tag )
                 | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
                 | ( "title" "=" quoted-string )
                 | ( "title*" "=" ext-value )
                 | ( "type" "=" ( media-type | quoted-mt ) )

Did not see concrete example in the spec, but seems like it should be something like this (however maybe with more relevant rel usage):

Link: </TheBook/chapter2>;
         rel="previous"; type="application/vnd.myBook.v3.abstract+json"; title*=UTF-8'de'letztes%20Kapitel,
         </TheBook/chapter4>;
         rel="next"; type="application/vnd.myBook.v3.firstPage+json"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel

This is also used for Server Push:

Link: </assets/font.woff2>; rel=preload; as=font; type='font/woff2'

So, would it be feasible to align with subset of Link spec here?

Example:

Preload: </member/*>; type="application/vnd.myBook.v3.abstract+json",
         </member/*/author>; type="application/vnd.myBook.v3.extendedAuthorBio+json"

Side: As shown in last example, media types can also be used for versioning the API gracefully over time on a per resource basis.

@andrerom
Copy link
Contributor Author

This was not really solved in #54 as that is more about doing a media type selector feature, and not so much about content / media type negotiation aspect.

But that said, I guess we can rather solve our need by making sure to expose additional links in our responses which can be used by Preload or expanded using Fields.

So this can stay closed for now.

@dunglas dunglas reopened this Jul 16, 2020
@dunglas
Copy link
Owner

dunglas commented Jul 16, 2020

Let’s reopen (it has been automatically closed by GitHub) until we describe a solution for all use cases.

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

Successfully merging a pull request may close this issue.

2 participants