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

How to keep service worker from intercepting WP REST requests? #1106

Open
reggieofarrell opened this issue Feb 10, 2024 · 9 comments
Open

How to keep service worker from intercepting WP REST requests? #1106

reggieofarrell opened this issue Feb 10, 2024 · 9 comments
Labels
bug Something isn't working service-workers
Milestone

Comments

@reggieofarrell
Copy link

We are using this plugin on showlists.net. It's a concert listing site. I recently ran into an issue where the service worker is intercepting WP REST requests and returning a 200 with what I assume is the 500 template. This was a custom WP REST endpoint of ours that a bug was introduced into. So the endpoint was actually returning a 500, but the user would have no idea that the request failed because the service worker is returning a 200 along with the 500 template HTML. That is having the effect of our error messaging not being shown because the request is getting back a 200 when it should be getting back a 500.

If I inspect the response from the service worker in Chrome, I see this in the Preview tab...

Screen Shot 2024-02-09 at 6 32 53 PM

Is there a way to configure the service worker to ignore calls to any routes that start with /wp-json?

@westonruter
Copy link
Collaborator

This should be the filter you're looking for:

/**
* Filter list of URL patterns to denylist from handling from the navigation router.
*
* Please note that the pattern is matched against the URL path concatenated with the query string.
* The URL origin (scheme, host, port) are not considered for matching in navigation requests.
*
* @since 0.4
* @link https://github.com/GoogleChrome/workbox/blob/5530e0b5d0b8d4da9f23747ecac665950a26bd51/packages/workbox-routing/src/NavigationRoute.ts#L103-L116
*
* @param array $denylist_patterns Denylist patterns.
*/
$denylist_patterns = apply_filters( 'wp_service_worker_navigation_route_denylist_patterns', $denylist_patterns );

Using plugin code like this:

add_filter(
	'wp_service_worker_navigation_route_denylist_patterns',
	static function ( $patterns ) {
		$patterns[] = '^\/wp-json';
		return $patterns;
	}
);

@reggieofarrell
Copy link
Author

@westonruter Thanks. I gave that a try and the initial request was still intercepted by the service worker which returned a 200 even though the endpoint returned a 500...

Screen Shot 2024-02-27 at 5 56 23 PM Screen Shot 2024-02-27 at 5 56 29 PM

I added the filter in a plugin where we have all of our custom back-end code. Does that filter need to be wrapped in another action potentially?

@westonruter
Copy link
Collaborator

No, it doesn't matter where you add the filter.

I see it showing up in your service worker:

	const denylist = ["^\\\/wp-json","^\\\/wp\\-admin($|\\?|\/)","^[^\\?]*?\\.php($|\\?)","\\?(.*?&)?wp_service_worker=","^[^\\?]*?\\\/wp\\.serviceworker(\\?|$)","^[^\\?]*?\\\/feed\\\/(\\w+\\\/)?$","\\?(.*?&)?wp_customize=","\\?(.*?&)?customize_changeset_uuid=","^\\\/wp\\-json\\\/"].map(
		(pattern) => new RegExp(pattern)
	);

Actually I even see wp-json appearing twice. This is because I forgot that the REST API was already supposed to be getting excluded from handling navigation requests:

// Exclude REST API (this only matters if you directly access the REST API in browser).
$denylist_patterns[] = '^' . preg_quote( wp_parse_url( get_rest_url(), PHP_URL_PATH ), '/' );

So no need to filter wp_service_worker_navigation_route_denylist_patterns as this is redundant.

How can I reproduce the issue you're seeing on https://austin.showlists.dev/ ?

@reggieofarrell
Copy link
Author

reggieofarrell commented Feb 28, 2024

@westonruter Yeah, I just looked at the code and noticed that line that was supposed to be excluding the REST api already as well. Hmmm. I can add you to our repo if you want to look at the code. I can't even recreate it locally because Chrome doesn't like the local SSL cert that was created by Local WP. So, the service worker doesn't load locally. I have to push to this staging site (.dev) in order to test anything PWA related which makes it kind of slow to diagnose. Right now I have the show submission endpoint on the .dev site just returning an error every time for the purpose of testing this.

@reggieofarrell
Copy link
Author

@westonruter to answer your question on how to reproduce...

  1. Create a custom REST endpoint that returns a 500
  2. Call it from the browser

You should see that the initial call get's intercepted by the service worker which then returns a 200 instead of the 500 that it should be returning.

@westonruter
Copy link
Collaborator

2. Call it from the browser

Call it in what way? By navigating to it or by requesting it with fetch()?

@reggieofarrell
Copy link
Author

@westonruter Sorry. Yes, requesting it with fetch() from a page where the service worker is loaded.

@westonruter
Copy link
Collaborator

OK, here is a plugin with which I can reproduce the issue:

<?php
/**
 * Plugin Name: Try 500 REST API error
 */

add_action( 'rest_api_init', static function () {
	register_rest_route(
		'try',
		'500-error',
		array(
			'methods' => array( 'GET', 'POST' ),
			'callback' => static function () {
				return new WP_REST_Response( 'I am an error', 500 );
			},
			'permission_callback' => '__return_true',
		)
	);
} );

add_action(
	'wp_footer',
	static function () {
		?>
		<script type="module">
			const url = <?php echo wp_json_encode( rest_url( '/try/500-error/' ) ); ?>;
			fetch( url, { method: 'POST' } ).then( ( response ) => {
				console.info( response );
				return response.json();
			} ).then( ( json ) => {
				console.info( json );
			} ).catch( ( error ) => {
				console.warn( error );
			} );
		</script>
		<?php
	}
);

I am seeing a log entry coming from console.warn():

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

The service worker is getting the 500 error response back as expected:

image

But then the service worker is incorrectly responding with the HTML error page:

image

Interestingly, if I change the request method from POST to GET then this issue doesn't happen. It's only the POST request method that causes it. And this is due to the service-worker-offline-post-request-handling.js logic which is matching all URLs:

wp.serviceWorker.routing.registerRoute(
/.*/,
offlinePostRequestHandler,
'POST'
);

Clearly this should be updated to pass through the body's response if the response's Content-Type is JSON. Either this, or it should do nothing if it is not handling a navigation request (i.e. it is a fetch).

Here is some plugin code with a quick fix to disable this functionality:

add_action(
	'wp_front_service_worker',
	static function ( WP_Service_Worker_Scripts $scripts  ) {
		unset( $scripts->registered['wp-offline-post-request-handling'] );
	},
	100
);

@westonruter westonruter added bug Something isn't working service-workers labels Mar 13, 2024
@westonruter westonruter added this to the 0.9 milestone Mar 13, 2024
@westonruter westonruter modified the milestones: 0.8.1, 0.9 Apr 2, 2024
@reggieofarrell
Copy link
Author

@westonruter that quick fix works for now. Thanks for your help.

jaredrethman added a commit to Automattic/newspack-plugin that referenced this issue May 28, 2024
jaredrethman added a commit to Automattic/newspack-plugin that referenced this issue May 31, 2024
jaredrethman added a commit to Automattic/newspack-plugin that referenced this issue Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working service-workers
Projects
None yet
Development

No branches or pull requests

2 participants