-
-
Notifications
You must be signed in to change notification settings - Fork 266
JSON Hyper Schema and HTTP Methods
Even what it says about draft-06 is incorrect, although the historical discussion of how method
changed over time may still be of theoretical interest
It looks like "method"
may be deleted in draft-wright-json-schema-hyperschema-01, a.k.a. Draft 06. If that happens, this page will be updated to cover how things are intended to work without "method"
. The current text describes what we had been planning for Draft 06 until encountering more problems with "method"
during the final pre-publication review.
Note that you can still use "method"
as an extension keyword, since conforming JSON Hyper-Schema implementations will just ignore it. Using either "method"
or "allow"
as an extension keyword is the preferred way to statically document exact HTTP usage until an official way can be decided upon.
For discussions on adding/restoring the ability to explicitly specify all HTTP methods in JSON Hyper-Schema, please see issue #73.
For discussions on describing non-standard usage of HTTP methods, please see issue #226.
Please do not derail other issues or PRs with these topics!
This page addresses the recent history and current state of identifying HTTP method usage in JSON Hyper-Schema. This topic has produced a lot of confusion and concern recently, which has derailed several discussions on how to move forward. In order to keep debates on Draft 06 and beyond focused, please read this history.
NOTE: This is @handrews's viewpoint and does not necessarily represent a consensus among project members.
Originally, hyper-schema link description objects (LDOs) were explicitly intended as analogous to HTML forms. This had the advantage of following the most widely-used approach to links that take user input. However, HTML forms were designed when GET and POST were the only usable HTTP methods. There has never been a need to add support for other methods, as they are usually handled by JavaScript when they are needed at all.
This approach has kept user input (via plain forms) separate from more general HTTP interactions (via JavaScript alone, or HTML forms plus JavaScript). This makes sense for interactive web pages, but API usage is more like JavaScript code than HTML. We need to clearly support all of HTTP, without specifically tying ourselves to that protocol.
First let's go over the HTML behavior, because as of Draft 06 it is once again the primary basis for JSON Hyper-Schema's approach. I cover the history, and how the widely-used Draft 04 is something of an exception, further down the page.
The "method"
keyword in JSON Hyper-Schema was originally identical to its HTML counterpart, and explicitly only allowed "GET" or "POST". However, the HTML5 form submission algorithm only defines a few scheme and method combinations:
-
http:
andhttps:
GET forms encode data into the URL query string. -
http:
andhttps:
POST forms submit the data as a payload. -
mailto:
GET forms send email using headers. -
mailto:
POST forms send email using a message body. -
data:
GET forms and allftp:
forms (both GET and POST) discard the data. -
data:
POST forms encode the data into the URI wherever certain character sequences appear.
This obviously limits how JSON Hyper-Schema links can be used. The problem is that URI schemes and the protocols they specify do not define how to map the corresponding protocols to the values "GET" and "POST". That mapping is defined by HTML. So if a given URI scheme is not of interest to the HTML standard, or is too new for HTML to have taken it into account, there is no clear way to make use of that URI scheme.
JSON Hyper-Schema has also always supported templated URIs with the "href"
keyword, although the exact templating syntax and processing rules have varied. This JSON Hyper-Schema feature is analogous to dynamically generating the form "action" URI on the server before sending the HTML to the browser.
Taking this approach separates user input from programmatic URI computation. In a human-oriented system such as HTML forms, this makes sense. But in a programmatic API environment, the distinction between user-supplied data and data from other sources is not always clear.
Maintaining such a separation is not necessarily desirable with an API. Some applications may gather input directly from users, while others may get their information from a database or configuration file. The server should have discretion over exactly whether and how external input is used with the link's URI.
The first four drafts of JSON Schema featured identical wording for the "method"
keyword:
This indicates which method should be used to access the target
resource. In an HTTP environment, this would be "GET" or "POST"
(other HTTP methods such as "PUT" and "DELETE" have semantics that
are clearly implied by accessed resources, and do not need to be
defined here). This defaults to "GET".
This wording makes it clear that PUT and DELETE (and presumably PATCH) would never appear as values for "method"
because of their well-defined semantics. It also implies that for protocols other than HTTP, values other than "GET" or "POST" could be used.
What's not clear is how to express that PUT, DELETE, or PATCH are supported. Nor is it clear how to indicate the PATCH request media type. Finally, URL query string parameters cannot be used with POST.
Draft 04 changed this so that the value of "method"
is the actual protocol method, while also noting the role that link relations (as specified in "rel"
) play in determining available and appropriate methods:
This property defines which method can be used to access the target
resource. In an HTTP environment, this might be "GET" or "POST" (or
other HTTP methods).
Some link relation values imply a set of appropriate HTTP methods to
be used for the link. For example, a client might assume that a link
with a relation of "edit" can be used in conjuction with the "PUT"
HTTP method. If the client does not know which methods might be
appropriate, then this SHOULD default to "GET".
With this definitions, if "method"
is not specified, the link relation is the primary indication of which HTTP methods are available. Since the default assumtion SHOULD be "GET", then by default if there is a "schema"
, it applies to URL query string paramters.
However, the behavior of "schema"
was still only defined in terms of "GET" or "POST". In particular, there is no indication of whether or how"schema"
applies to a "method"
of "PUT" or "PATCH". Many schema authors assume that it applies to the request body, but that is neither clearly specified nor clearly forbidden.
If "schema"
applies to the request body for "PUT" and "PATCH", then we lost the ability from Draft 03 and earlier to use "PUT" or "PATCH" with URL query parameters. And we still cannot use URL query parameters with POST.
Draft 04's long (if unorthodox) life span means that this is how most people familiar with JSON Schema expect things to work. While that changed back to an earlier approach, that early approach is no longer widely known, creating concern over the subsequent changes.
Draft 05 returned to only allowing "get" or "post". These names were now lower-case to suggest that, as in HTML, they may indicate something other than a literal HTTP GET or POST depending on the scheme. Other values are technically allowed, but SHOULD be ignored.
This property specifies that the client can construct a templated
query or non-idempotent request to a resource.
If "method" is "get", the link identifies how a user can compute the
URI of an arbitrary resource. For example, how to compute a link to a
page of search results relating to the instance, for a user-selected
query term. Despite being named after GET, there is no constraint on
the method or protocol used to interact with the remote resource.
If "method" is "post", the link specifies how a user can construct a
document to submit to the link target for evaluation.
Values for this property SHOULD be lowercase, and SHOULD be compared
case-insensitive. Use of other values not defined here SHOULD be
ignored.
The language about link relations was dropped, but language was added to clearly indicate that other HTTP methods could be used to interact with the URI constructed from input to a "get" link. The link relation specification already indicates that link relations can imply HTTP method support, so we do not need to repeat that here.
An important related change was made to "targetSchema"
which now reads:
This property provides a schema that is expected to describe the link target, including what a client can expect if it makes an HTTP GET request, and what it should send if it replaces the resource in an HTTP PUT request. This property is advisory only.
The guidance on use with PUT in particular is new compared to both Draft 04 and Draft 03.
Overall this is similar to the Draft 03 behavior, with similar limitations but more clarity around using other methods. We can once again use URL query string parameters with PUT, PATCH, and DELETE, but we also once again have no way to indicate which methods are supported. And we still cannot use them with POST. Presence of a "targetSchema"
tells us how to use PUT, but not whether it is allowed.
A major change from Draft 03 is the explicit statement that the values of "method"
are not protocol-specific. All protocols are expected to map from "get" and "post".
The only real effect of "method"
for HTTP links in Draft 05 is to determine how to use "schema"
and "encType"
. A value of "get" applies these to the URL query string, while a value of "post" applies them to the request body.
This is important to keep in mind for the next set of changes: we are working from a "get"/"post" world in Draft 05, not from Draft 04's explicit method approach.
Draft 06 keeps the same language as Draft 05 for "method"
and "targetSchema"
, but makes a significant change by adding "hrefSchema"
as a schema for the URI Template variables from "href"
.
Previously, the URI Template variables were viewed as analogous to server-side dynamic construction of the target URI. This is still supported, but external input is now allowed to override it. The philosophical justification is that API usage is different from interactive HTML, and being able to move user input to different parts of the URI aids HATEOAS (for instance, moving a value from a nested path component to a query parameter for flexibility). Browsers have no mechanism to encode data anywhere else in the URI, so this has never been a concern for HTML.
If there is no "hrefSchema"
, everything behaves as in Draft 05, with the server fully in control of the URI. The server can choose how much latitude to give to users by only allowing input on some URI Templates. The differences between "hrefSchema"
vs "schema"
with a "method"
of "get" are:
- Input can be placed anywhere in the URI, not just form-encoded query parameter values.
- Input capability is no longer tied to the choice of
"method"
.
"method"
and "schema"
behave the same as in Draft 05, including overriding the resolved URI Template query string whether "hrefSchema"
was involved in resolving the template or not.
There is still much to be done in this area. Topics include:
- A way to hint at HTTP method support (Issue #73)
- This could be a redefined
"method"
keyword, or something else such as"allow"
- This could be a list of method names to avoid needing to repeat links
- Like
"targetSchema"
and"mediaType"
, it would be advisory only; the resource itself is the only authoritative source
- This could be a redefined
- A way to hint at the PATCH request media type (not yet filed)
- Equivalent to the
Accept-Patch
header - Also advisory, the resource can set
Accept-Patch
differently at runtime (although this would arguably be bad design)
- Equivalent to the
- A way to resolve URI Template variables from locations in the instance other than top-level properties or array elements
- A way to map instance data into a request body (the equivalent of server-supplied "hidden" form inputs)