From f784a0481bee163729b665198187ebdced1e3178 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 3 Nov 2016 04:04:41 +0000 Subject: [PATCH] REST API: Return error when JSON decoding fails. If you send a request to the REST API with invalid JSON in body than it will now return a error. This assists developers if they accidentally send invalid JSON and wonder why their data appears to be ignored. Props rmccue. Fixes #38547. git-svn-id: https://develop.svn.wordpress.org/trunk@39109 602fd350-edb4-49c9-b593-d223f7449a82 --- .../rest-api/class-wp-rest-request.php | 23 ++++++++++++++++--- tests/phpunit/tests/rest-api/rest-request.php | 15 ++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/rest-api/class-wp-rest-request.php b/src/wp-includes/rest-api/class-wp-rest-request.php index d3e8c755af..734ee982fc 100644 --- a/src/wp-includes/rest-api/class-wp-rest-request.php +++ b/src/wp-includes/rest-api/class-wp-rest-request.php @@ -651,11 +651,13 @@ class WP_REST_Request implements ArrayAccess { * Avoids parsing the JSON data until we need to access it. * * @since 4.4.0 + * @since 4.7.0 Returns error instance if value cannot be decoded. * @access protected + * @return true|WP_Error True if the JSON data was passed or no JSON data was provided, WP_Error if invalid JSON was passed. */ protected function parse_json_params() { if ( $this->parsed_json ) { - return; + return true; } $this->parsed_json = true; @@ -664,7 +666,7 @@ class WP_REST_Request implements ArrayAccess { $content_type = $this->get_content_type(); if ( empty( $content_type ) || 'application/json' !== $content_type['value'] ) { - return; + return true; } $params = json_decode( $this->get_body(), true ); @@ -676,10 +678,19 @@ class WP_REST_Request implements ArrayAccess { * might not be defined: https://core.trac.wordpress.org/ticket/27799 */ if ( null === $params && ( ! function_exists( 'json_last_error' ) || JSON_ERROR_NONE !== json_last_error() ) ) { - return; + // Ensure subsequent calls receive error instance. + $this->parsed_json = false; + + $error_data = array( + 'status' => WP_Http::BAD_REQUEST, + 'json_error_code' => json_last_error(), + 'json_error_message' => json_last_error_msg(), + ); + return new WP_Error( 'rest_invalid_json', __( 'Invalid JSON body passed.' ), $error_data ); } $this->params['JSON'] = $params; + return true; } /** @@ -841,6 +852,12 @@ class WP_REST_Request implements ArrayAccess { * WP_Error if required parameters are missing. */ public function has_valid_params() { + // If JSON data was passed, check for errors. + $json_error = $this->parse_json_params(); + if ( is_wp_error( $json_error ) ) { + return $json_error; + } + $attributes = $this->get_attributes(); $required = array(); diff --git a/tests/phpunit/tests/rest-api/rest-request.php b/tests/phpunit/tests/rest-api/rest-request.php index 47d4c32c8f..de44844bff 100644 --- a/tests/phpunit/tests/rest-api/rest-request.php +++ b/tests/phpunit/tests/rest-api/rest-request.php @@ -399,6 +399,21 @@ class Tests_REST_Request extends WP_UnitTestCase { $this->assertEquals( 'rest_invalid_param', $valid->get_error_code() ); } + public function test_has_valid_params_json_error() { + if ( version_compare( PHP_VERSION, '5.3', '<' ) ) { + return $this->markTestSkipped( 'JSON validation is only available for PHP 5.3+' ); + } + + $this->request->set_header( 'Content-Type', 'application/json' ); + $this->request->set_body( '{"invalid": JSON}' ); + + $valid = $this->request->has_valid_params(); + $this->assertWPError( $valid ); + $this->assertEquals( 'rest_invalid_json', $valid->get_error_code() ); + $data = $valid->get_error_data(); + $this->assertEquals( JSON_ERROR_SYNTAX, $data['json_error_code'] ); + } + public function test_has_multiple_invalid_params_validate_callback() { $this->request->set_url_params( array( 'someinteger' => '123',