[wp-trac] [WordPress Trac] #47676: Add support for If-Unmodified-Since header to make a conditional request when updating posts via REST API (for offline and collaborative editing)
WordPress Trac
noreply at wordpress.org
Thu Jul 11 19:23:39 UTC 2019
#47676: Add support for If-Unmodified-Since header to make a conditional request
when updating posts via REST API (for offline and collaborative editing)
--------------------------------------+------------------------------
Reporter: westonruter | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Editor | Version: 4.7
Severity: normal | Resolution:
Keywords: has-patch has-unit-tests | Focuses: rest-api
--------------------------------------+------------------------------
Comment (by westonruter):
Replying to [comment:2 TimothyBlynJacobs]:
> Why `If-Unmodified-Since` instead of using ETag's? An ETag could be
built by serializing the item response.
Good question. I didn't think about that. One issue with using ETag
generated from serializing the response is that it would require always
querying data for all fields, even if they are not requested by the user.
For example, if someone had obtained the entity originally via `/wp-
json/wp/v2/posts/2553?_fields=id,title,link` and then they modify the
`title` and `PUT` back the modified post, then the ETag would not have
been generated to include the `content`. Or perhaps this doesn't matter
because you only requested the `title` in the first place?
> That would handle updates to the entity that don't trigger
`post_modified` like metadata, terms, and any other custom fields.
> I suppose if we wanted to get similar benefits, we could force a
`post_modified` update whenever a post entity is updated through the posts
controller.
That is a very good point. For custom fields, the only thing to protect
against that would be to store some additional `last_modified` in postmeta
and bump it whenever postmeta is changed or term associations are changed,
such as via the posts controller. But that's not ideal.
> Or is the fact that `post_modified` would only track changes to the post
resource a benefit?
The `post_modified` would probably be an 80% solution.
> If the `If-Unmodified-Since` is used I think we should definitely send
`Last-Modified` as a header, and ideally support `If-Modified-Since` as
well. `Last-Modified` can also be sent as a [https://json-
schema.org/latest/json-schema-hypermedia.html#rfc.section.6.5.5
targetHint] so it is accessible in the collection route.
Another great point. Yes, sending the `Last-Modified` header as derived
from `post_modified_gmt` is something I missed. A client should not be
trying to convert the `post_modified_gmt` into a value to for the `If-
Unmodified-Since` header.
> One downside to an ETag is it'd be more intensive to calculate.
I see it being more intensive in two ways:
1. The `get_item` method would need to compute the `ETag` from serializing
the response data.
2. The `update_item` method would need to call `get_item` if the request
has an `If-Match` header, and then compare the `get_item` method's
response `ETag` with the `PUT` request's `If-Match` header to see if there
is a match.
In other words:
{{{#!diff
--- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-
controller.php
+++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-
controller.php
@@ -477,6 +477,12 @@ class WP_REST_Posts_Controller extends
WP_REST_Controller {
$response->link_header( 'alternate',
get_permalink( $post->ID ), array( 'type' => 'text/html' ) );
}
+ $response->set_headers(
+ array(
+ 'ETag' => md5( serialize(
$response->get_data() ) ),
+ )
+ );
+
return $response;
}
@@ -685,6 +691,17 @@ class WP_REST_Posts_Controller extends
WP_REST_Controller {
return $valid_check;
}
+ // Handle conditional request.
+ if ( $request->get_header( 'If-Match' ) ) {
+ $get_request = clone $request;
+ $get_request->set_method( 'GET' );
+ $get_request->set_body( null );
+ $etag = md5( serialize( $this->get_item(
$get_request )->get_data() ) );
+ if ( $etag !== trim( $request->get_header( 'If-
Match' ), '"' ) ) {
+ return new WP_Error(
'rest_precondition_failed', __( 'Post has been changed on server. Please
resolve conflicts and try again' ), array( 'status' => 412 ) );
+ }
+ }
+
$post = $this->prepare_item_for_database( $request );
if ( is_wp_error( $post ) ) {
}}}
--
Ticket URL: <https://core.trac.wordpress.org/ticket/47676#comment:3>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list