Closed (fixed)
Project:
Drupal core
Version:
8.7.x-dev
Component:
rest.module
Priority:
Major
Category:
Plan
Assigned:
Unassigned
Reporter:
Created:
31 May 2016 at 19:30 UTC
Updated:
1 Jan 2019 at 16:44 UTC
Jump to comment: Most recent
It's very hard to understand routing in the REST module, and it's very brittle, because:
ResourceBase only sets _format for GET route, should also do so for POST/PATCH/DELETE routes. — fixed by #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistentResourceBase generates one route per method for POST/PATCH/DELETE, but one route per method+format for GET. This should be made consistent. — fixed by #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistentResourceBase generates individual GET routes for every available serialization format, not just the ones that are allowed by the configuration, and then ResourceRoutes::alter() removes those that are not acceptable by the configuration. — fixed by #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistentResourceBase sets _content_type_format for PATCH/POST, but it should actually also set _format. — fixed by #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistentResourceBase sets _content_type_format not to the allowed formats for that particular REST resource (as per the rest.settings configuration), but to all acceptable serialization formats, and then has validation for it in \Drupal\rest\RequestHandler::handle(). — fixed by #2826407: PATCH + POST allowed format validation happens in RequestHandler::handle(), rather than in the routing systemResourceRoutes::alter() is doing configuration verification. — fixed by #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistentResourceRoutes::alter() assumes _format has only ever one format listed, even though multiple can be listed. This blows up as soon as any custom @RestResource plugin specifies multiple formats, which it can by not using ResourceBase::routes(). — fixed by #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistentResourceRoutes::alter() sets _csrf_request_header_token on all routes, including GET routes. This is harmless, but it causes extra, unnecessary computations on those requests. And it makes the route more confusing to debug. — fixed by #2869426: EntityResource should add _entity_access requirement to REST routesRequestHandler::handle() assumes _format is always set, and always has a single value — and in case it's not set, its ultimate solution is "just pick JSON". But it's not guaranteed to have a single value… nor does it even always have a value. And in fact, PATCH/POST routes do not have _format set. So any PATCH/POST request will actually result in a JSON response, even if you explicitly specify ?_format=xml!RequestHandler::handle() instead of in a KernelEvents::EXCEPTION event subscriber. This prevents Basic Auth 401 responses from being sent, and means REST has its own system in parallel to Symfony's/Drupal core's.RequestHandler is hard to understand, and is messy. — fixed by #2853460: Simplify RequestHandler: make it no longer ContainerAware, split up ::handle()RequestHandler has fairly complex logic to deal with HEAD requests. — fixed by #2775479: Try to remove the "map HEAD to GET" logic in \Drupal\rest\RequestHandler::handle()Clearly, this leaves us in a state where we keep finding bizarre bugs.
Refactor REST's routing for better maintainability.
See child issues.
Comments
Comment #2
wim leersAdded another example, originating from #2662284-33: Return complete entity after successful PATCH.
Comment #3
wim leersComment #4
klausi_format is the Accept header a client sends. It is irrelevant on POST/PATCH/DELETE because drupal will look at the Content-type header that is set for the data that comes with the request. That's also the reason why there are multiple routes for GET.
Comment #5
klausiOh, and for DELETE _format and _content_type_format is irrelevant because no data will be send or received from the server.
Comment #6
dawehnerWhile fixing things here is nice, we should always try to ensure that we don't break any existing plugins out there. This could be always quite tricky.
Comment #7
wim leers#4: Well, it's totally valid to send data in one format and request it in a different format. And is simply untrue: it uses that format only for deserializing incoming data, not for the response. The REST module does not currently support POST/PATCH requests that have actual response bodies, and not just a status, and it's because of that that this was not noticed until #2546216: Return entity object in REST response body after successful POST and #2662284: Return complete entity after successful PATCH. So it's understandable that it's in the state it is in, but it's not correct.
EDIT: from
RequestHandler::handle():#5: I also don't know a use case for DELETE, but perhaps there is a valid use case to have a request or response body for DELETE requests? This is the one I'm least concerned about for sure.
#6: This is indeed my biggest fear, that we must continue to emulate the current bugs/quirks to not break BC.
Comment #8
dawehnerLet's make that a plan and work on small things which certainly don't break BC.
Comment #9
wim leersAdded more examples, originating from #2664780-54: Remove REST's resource- and verb-specific permissions for EntityResource, but provide BC and document why it's necessary for other resources.
Comment #10
kriboogh commentedJust out of curiosity, why the _format query parameter on a GET request? can't this be extracted from the Accept header, cause that's where you specify your desired format no?
Comment #11
wim leers@kriboogh: See https://www.drupal.org/node/2501221.
Comment #13
wim leersThis was fixed by the combination of:
One less thing to do here :)
Comment #14
wim leersComment #15
wim leersThis was fixed by #2662284: Return complete entity after successful PATCH.
Comment #16
wim leers#2753681: Move CSRF header token out of REST module so that user module can use it, as well as any contrib module caused a change in this one. It's now
_csrf_request_header_token. The root cause has not been addressed yet though. IS updated.Comment #17
wim leersI checked it again, and points 1 through 8 all still need to be addressed.
Comment #19
wim leersThis is being worked on at #2826407: PATCH + POST allowed format validation happens in RequestHandler::handle(), rather than in the routing system.
Comment #20
wim leersAdded:
Comment #21
wim leersAdded:
Comment #22
wim leers#2775479: Try to remove the "map HEAD to GET" logic in \Drupal\rest\RequestHandler::handle() landed.
Comment #23
wim leersOpened #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistent to fix points 1+2+3+4 and 6+7. Why fix so many points in a single issue? Because they're all very much related; they all depend on the same few lines of code in
ResourceBase.This means that only point 8 does not yet have an issue to fix it:
I'm also removing point 13, with the ellipsis. Because once the first 12 issues are done, I think REST routing is sufficiently non-brittle.
Comment #24
wim leersComment #25
wim leers#2858482 was RTBC, but as of #2858482-26: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistent, it's now blocked on #2472337: Provide a lazy alternative to service collectors which just detects service IDs. Which means #2853460 is now blocked on two issues.
Comment #26
wim leersPer #25.
Comment #27
wim leersNote this is a blocker to #2822201: Allow contrib modules to deploy REST resources by creating routes, removing the need for config entities in that special case, which is a . So this is too.
Comment #28
wim leers#2472337: Provide a lazy alternative to service collectors which just detects service IDs landed, #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistent has been rerolled and is now blocked on review. That's the next step in unblocking this!
Comment #30
wim leersAs of #2858482-39: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistent, #2858482 is blocked on another issue: #2883680: Force all route filters and route enhancers to be non-lazy. This therefore has a chain of 3 issues blocking it.
Comment #31
wim leersThis is the only one I didn't have an issue for yet:
ResourceRoutes::alter()sets_csrf_request_header_tokenon all routes, including GET routes. This is harmless, but it causes extra, unnecessary computations on those requests. And it makes the route more confusing to debug.But as of #2869426-28: EntityResource should add _entity_access requirement to REST routes, that now has an issue+patch, and it's actually proven to be less harmless than I thought.
Comment #32
wim leers#2826407: PATCH + POST allowed format validation happens in RequestHandler::handle(), rather than in the routing system landed, bringing it down to PP-2.
But as of #31, #2869426: EntityResource should add _entity_access requirement to REST routes was added, so we remain at PP-3.
Also updated #2905563: #2905563-26: REST: top priorities for Drupal 8.5.x
Comment #33
wim leersForgot to strike through point 5, per #32.
Comment #34
wim leers#2853460: Simplify RequestHandler: make it no longer ContainerAware, split up ::handle() + #2858482: Simplify REST routing: disallow requesting POST/PATCH in any format, make consistent landed!
That only leaves #2869426: EntityResource should add _entity_access requirement to REST routes.
Comment #37
wim leersYAY!!!!!!! #2869426: EntityResource should add _entity_access requirement to REST routes just landed! Which means this is now done!