Currently, in order to use JSON format on GET request, REST API requires ?_format=json.

I suggest introducing a default_format per resource to be used when no ?_format=json is provided.

Proposed format for config/install yaml:

id: demo
langcode: de
status: true
plugin_id: demo
granularity: resource
configuration:
  methods:
    - GET
  default_format: json
  formats:
    - json
  authentication:
    - cookie
dependencies:
  module:
    - serialization
Members fund testing for the Drupal project. Drupal Association Learn more

Comments

maosmurf created an issue. See original summary.

maosmurf’s picture

FileSize
3.5 KB

By applying the provided patch, a REST resource will use the format from default_format, if no ?_format=json was specified.

Wim Leers’s picture

Technically, #2821701: Default '_format' for REST resources routes predates this by a long shot. But it was in the wrong issue queue. So closed that in favor of this.

Its IS said:

I created a RestResource plugin that exposes some data in JSON format. The problem I'm having is that when I access the defined URL for that plugin, I also have to specify the "_format=json" parameter.

Is there a way to alter the default '_format' so that it's json, instead of html, so I can ommit passing it in the URL?

And @dawehner remarked this was kind of similar to #2831137: Remove the need for ?_format=api_json: assume the use of the 'api_json' format for routes managed by JSON API for the JSON API module.

Wim Leers’s picture

Version: 8.2.6 » 8.4.x-dev
Issue tags: -REST API, -Drupal 8, -format, -json +API-First Initiative

@maosmurf: thanks for reporting this issue, and for the patch!

While I understand this makes things easier, this also comes with some consequences:

  1. when the default format configuration is changed, then all user agents (browsers, but also intermediaries such as proxies and reverse proxies) will have cached the previous format
  2. this violates RESTful principles
  3. this especially violates RESTful principles because the REST module is specifically designed to expose resources in multiple formats
  4. this also won't help for many resources that already have a HTML route: GET /node/1 will still return the HTML representation, not the JSON representation. Which means this would only work for some REST resources. Which is very confusing.

I'd like to do say "YES!", but the downsides above almost force me to politely decline…

For the JSON API module this is different, because it's specfically exposing a different (path-prefixed) URL structure and is designed to only ever support one format. So that module can get away with it, but for the REST module this is not a great fit.

So, I think what would make most sense is to create a contributed module that gives you this ability. Storing the default format per resource is exactly what the third_party_settings key in Config Entities' config schema exists for, so it's perfectly possible!

That's my vote: make this a third-party module.

What do others think?

Wim Leers’s picture

Title: Allow default format for REST endpoints » Allow default format for REST resources

Also, Crell and other "RESTful/HTTP purists" would strongly condemn the use of the term "endpoints" here :P

drpal’s picture

@maosmurf

I think this is a great idea, however I have to agree with @Wim Leers on this. The inconsistencies this would introduce would be a bit too painful. I think the suggestion of creating a contrib module to support this behavior though is incredibly valuable.

Crell’s picture

True story, Wim. :-)

I would largely agree with Wim in #4. Were we using HTTP headers as we originally intended (before we realized just how badly broken browsers and caches were), the format would be required (as a header). In case it was missing the routing system itself, I believe, would assume text/html, because that is in practice what most of Drupal's responses are. Changing the default, or making the default sometimes one thing, sometimes another, but as an external user you can't tell which is which introduces a BC change and/or a much uglier and less predicable DX.

-1

klausi’s picture

Agreed with Wim. Having a default format does only make sense for routes that don't serve HTML. Otherwise the default format will be HTML (for example /node/1).

Instead, I would propose that if there is no HTML route on the same path then REST module should serve just the first format that is defined per default. That way you get your JSON if you omit the _format in the URL. That would also avoid useless 406 Not Acceptable responses when there are only REST routes on the path anyway. Just serve anything instead of an error, right? This also does not violate the HTTP spec where it says that you SHOULD return a 406 when you can't comply to the specified format. A SHOULD is not a MUST, so I think we can just throw JSON in your face if you can't specify formats :)

e0ipso’s picture

I think that, if anything, we should allow to drop _format if there is only one format supported for a given route and it's not HTML. So +1 to allowing a default format per route, although maybe only in some contexts.

The real challenge is how to use that format for errors that happen before the route has been matched to fetch the default format. I'm thinking about a route that sets its default to json but an error happens before we can know that about the route. In that case the error would not be json, but html. This error formatting business feels like an edge case and it should not preclude us from having a very useful feature.

Wim Leers’s picture

Instead, I would propose that if there is no HTML route on the same path then REST module should serve just the first format that is defined per default. That way you get your JSON if you omit the _format in the URL.

But this means that if the configuration changes, or even if just the order of formats in the configuration changes, the default changes. In other words: the concern I raised in #4.1:

  1. when the default format configuration is changed, then all user agents (browsers, but also intermediaries such as proxies and reverse proxies) will have cached the previous format

So IMHO we can't do that either. Thoughts?


The real challenge is how to use that format for errors that happen before the route has been matched to fetch the default format. I

Failing so early means we can't really do anything about it. I'm not concerned about this aspect at all: that would not block this from happening! :)

Crell’s picture

The trick with #8 is that we then need to determine if there is also an HTML route, in which case we could default to the first entry/only entry. I don't know off hand how easy that is.

Also, REST module may not be the only one that's adding a route at a given path. It may only be adding one JSON route, but there's nothing preventing an entirely different module adding its own, say, SVG route at the same path, which REST module would not reliably be able to detect. That's true even if we're not talking about Entities, which we know add an HTML route.

In practice I don't know that a default is going to be possible.

dawehner’s picture

If we want to support the general possiblity to do that, we should expose that somehow in the routing system. We already know how fragile it is, so suggesting contrib to figure out the problem seems kind of ignorant.

I totally think we should though not automagically adapt the behaviour of existing routes. Explicit URLs, either via a prefix (like jsonapi) or via _format IMHO is for itself a good thing.

Failing so early means we can't really do anything about it. I'm not concerned about this aspect at all: that would not block this from happening! :)

As far as I understand @e0ipso cares about 401/403/404 errors, as they ideally would be rendered in a jsonapi format as well?

Did we ever considered again to explore the idea of path prefix for our rest APIs, like /api. Seems wrong to start the discussion again.

maosmurf’s picture

TL;DR

default_format is optional and must be set explicitely (i.e. per REST resource), so introduction does not break existing behaviour.
It only gives the possibility to set a default explicitely for the routes that we want to have a default.

First and most, thank you for your feedback (especially, as this is my first issue on drupal.org)

@Wim:

While I understand this makes things easier, this also comes with some consequences:

1. when the default format configuration is changed, then all user agents (browsers, but also intermediaries such as proxies and reverse proxies) will have cached the previous format
2. this violates RESTful principles
3. this especially violates RESTful principles because the REST module is specifically designed to expose resources in multiple formats
4. this also won't help for many resources that already have a HTML route: GET /node/1 will still return the HTML representation, not the JSON representation. Which means this would only work for some REST resources. Which is very confusing.

ad 1) As there is no default currently, I don't see an issue regarding backward-compatibility. If defautl is changed later on, one obviously would have to deal with consequences.
ad 3) we still can expose in different formats, the ?format parameter remains active and so do all URLs with that parameter. By specifying a default format, we technically only add a new URL.
ad 4) That's ok, just don't add a default_format to node routing.

@klausi

Agreed with Wim. Having a default format does only make sense for routes that don't serve HTML. Otherwise the default format will be HTML (for example /node/1).

No problem, just don't define a default_format on your HTML route.

Instead, I would propose that if there is no HTML route on the same path then REST module should serve just the first format that is defined per default.

Although we also had this idea, we decided that relying on the order of things always bears the risk of breaking.

@e0ipso

I think that, if anything, we should allow to drop _format if there is only one format supported for a given route and it's not HTML. So +1 to allowing a default format per route, although maybe only in some contexts.

Great idea, although we could drop the explicit default_format then altogether? Would this break current 406 errors?

The real challenge is how to use that format for errors that happen before the route has been matched to fetch the default format.

The patch affects RequestFormatRouteFilter, so from my understanding, the problem here is a more general one and not limited to this issue. Which format do you use now, before RequestFormatRouteFilter kicks in (with or without the patch)?

This error formatting business feels like an edge case and it should not preclude us from having a very useful feature.

Same thought here, but error handling is crucial nevertheless, so I'd love to discuss how my patch affects retrieval of error format (or lack thereof).

@Crell

The trick with #8 is that we then need to determine if there is also an HTML route, in which case we could default to the first entry/only entry. I don't know off hand how easy that is.

Just don't set a default_format for your routes that also have HTML.

Changing the default, or making the default sometimes one thing, sometimes another, ...

On GET /node/1 I expect HTML, whereas on GET /api/v1/foo/bar I expect JSON.

but as an external user you can't tell which is which

None forces you to set default_format on your routes.

introduces a BC change

As long as you don't specify default_format on your route, nothing will change.
The feature only kicks in, when you explicitely add default_format to one particular route.

much uglier and less predicable DX.

Let's not start (yet another) discussion about requiring ?_format=json

In practice I don't know that a default is going to be possible.

default_format is per-route, not system-wide!
We run a drupal instance with by patch applied, where we obviously added default_format only to our JSON-only routes.

@dawehner

I totally think we should though not automagically adapt the behaviour of existing routes.

Absolutely true, that's why adding functionallity of default_format does not change anything, as long as you do not add default_format explicitely to the routes you'd like to have a default.

Wim Leers’s picture

As there is no default currently, I don't see an issue regarding backward-compatibility. If defautl is changed later on, one obviously would have to deal with consequences.

The current behavior when you omit ?_format is to always return a 4xx response. This would change that to a 200 response.

None forces you to set default_format on your routes.

Now, you're talking about routes. But this is filed against the REST module. And the IS also explicitly mentions the REST module's configuration. This would be okay to add as a feature to the routing system. But adding it to the REST module is what would be problematic.

Absolutely true, that's why adding functionallity of default_format does not change anything, as long as you do not add default_format explicitely to the routes you'd like to have a default.

Hm, so it sounds like you are proposing to optionally allow REST resource config entities to specify default_format, but to not automatically set it by default. So for example, we would automatically update the node REST resource config entity from:

langcode: en
status: true
dependencies:
  module:
    - basic_auth
    - hal
    - node
id: entity.node
plugin_id: 'entity:node'
granularity: resource
configuration:
  methods:
    - GET
    - POST
    - PATCH
    - DELETE
  formats:
    - hal_json
  authentication:
    - basic_auth

to:

langcode: en
status: true
dependencies:
  module:
    - basic_auth
    - hal
    - node
id: entity.node
plugin_id: 'entity:node'
granularity: resource
configuration:
  methods:
    - GET
    - POST
    - PATCH
    - DELETE
  default_format: false
  formats:
    - hal_json
  authentication:
    - basic_auth

But then a particular site is able to mark a particular format as the default.

This way, BC is kept, we just add a new capability.

Is this a correct representation of what you would like to see?

hideaway’s picture

reroll of #2 for 8.3.0

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.