WP_REST_Server::serve_request()publicWP 4.4.0

Handles serving a REST API request.

Matches the current server URI to a route and runs the first matching callback then outputs a JSON representation of the returned value.

Метод класса: WP_REST_Server{}

Возвращает

null|false. Null if not served and a HEAD request, false otherwise.

Использование

$WP_REST_Server = new WP_REST_Server();
$WP_REST_Server->serve_request( $path );
$path(строка)
The request route. If not set, $_SERVER['PATH_INFO'] will be used.
По умолчанию: null

Заметки

Список изменений

С версии 4.4.0 Введена.

Код WP_REST_Server::serve_request() WP 6.5.2

public function serve_request( $path = null ) {
	/* @var WP_User|null $current_user */
	global $current_user;

	if ( $current_user instanceof WP_User && ! $current_user->exists() ) {
		/*
		 * If there is no current user authenticated via other means, clear
		 * the cached lack of user, so that an authenticate check can set it
		 * properly.
		 *
		 * This is done because for authentications such as Application
		 * Passwords, we don't want it to be accepted unless the current HTTP
		 * request is a REST API request, which can't always be identified early
		 * enough in evaluation.
		 */
		$current_user = null;
	}

	/**
	 * Filters whether JSONP is enabled for the REST API.
	 *
	 * @since 4.4.0
	 *
	 * @param bool $jsonp_enabled Whether JSONP is enabled. Default true.
	 */
	$jsonp_enabled = apply_filters( 'rest_jsonp_enabled', true );

	$jsonp_callback = false;
	if ( isset( $_GET['_jsonp'] ) ) {
		$jsonp_callback = $_GET['_jsonp'];
	}

	$content_type = ( $jsonp_callback && $jsonp_enabled ) ? 'application/javascript' : 'application/json';
	$this->send_header( 'Content-Type', $content_type . '; charset=' . get_option( 'blog_charset' ) );
	$this->send_header( 'X-Robots-Tag', 'noindex' );

	$api_root = get_rest_url();
	if ( ! empty( $api_root ) ) {
		$this->send_header( 'Link', '<' . sanitize_url( $api_root ) . '>; rel="https://api.w.org/"' );
	}

	/*
	 * Mitigate possible JSONP Flash attacks.
	 *
	 * https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
	 */
	$this->send_header( 'X-Content-Type-Options', 'nosniff' );

	/**
	 * Filters whether the REST API is enabled.
	 *
	 * @since 4.4.0
	 * @deprecated 4.7.0 Use the {@see 'rest_authentication_errors'} filter to
	 *                   restrict access to the REST API.
	 *
	 * @param bool $rest_enabled Whether the REST API is enabled. Default true.
	 */
	apply_filters_deprecated(
		'rest_enabled',
		array( true ),
		'4.7.0',
		'rest_authentication_errors',
		sprintf(
			/* translators: %s: rest_authentication_errors */
			__( 'The REST API can no longer be completely disabled, the %s filter can be used to restrict access to the API, instead.' ),
			'rest_authentication_errors'
		)
	);

	if ( $jsonp_callback ) {
		if ( ! $jsonp_enabled ) {
			echo $this->json_error( 'rest_callback_disabled', __( 'JSONP support is disabled on this site.' ), 400 );
			return false;
		}

		if ( ! wp_check_jsonp_callback( $jsonp_callback ) ) {
			echo $this->json_error( 'rest_callback_invalid', __( 'Invalid JSONP callback function.' ), 400 );
			return false;
		}
	}

	if ( empty( $path ) ) {
		if ( isset( $_SERVER['PATH_INFO'] ) ) {
			$path = $_SERVER['PATH_INFO'];
		} else {
			$path = '/';
		}
	}

	$request = new WP_REST_Request( $_SERVER['REQUEST_METHOD'], $path );

	$request->set_query_params( wp_unslash( $_GET ) );
	$request->set_body_params( wp_unslash( $_POST ) );
	$request->set_file_params( $_FILES );
	$request->set_headers( $this->get_headers( wp_unslash( $_SERVER ) ) );
	$request->set_body( self::get_raw_data() );

	/*
	 * HTTP method override for clients that can't use PUT/PATCH/DELETE. First, we check
	 * $_GET['_method']. If that is not set, we check for the HTTP_X_HTTP_METHOD_OVERRIDE
	 * header.
	 */
	$method_overridden = false;
	if ( isset( $_GET['_method'] ) ) {
		$request->set_method( $_GET['_method'] );
	} elseif ( isset( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ) ) {
		$request->set_method( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] );
		$method_overridden = true;
	}

	$expose_headers = array( 'X-WP-Total', 'X-WP-TotalPages', 'Link' );

	/**
	 * Filters the list of response headers that are exposed to REST API CORS requests.
	 *
	 * @since 5.5.0
	 * @since 6.3.0 The `$request` parameter was added.
	 *
	 * @param string[]        $expose_headers The list of response headers to expose.
	 * @param WP_REST_Request $request        The request in context.
	 */
	$expose_headers = apply_filters( 'rest_exposed_cors_headers', $expose_headers, $request );

	$this->send_header( 'Access-Control-Expose-Headers', implode( ', ', $expose_headers ) );

	$allow_headers = array(
		'Authorization',
		'X-WP-Nonce',
		'Content-Disposition',
		'Content-MD5',
		'Content-Type',
	);

	/**
	 * Filters the list of request headers that are allowed for REST API CORS requests.
	 *
	 * The allowed headers are passed to the browser to specify which
	 * headers can be passed to the REST API. By default, we allow the
	 * Content-* headers needed to upload files to the media endpoints.
	 * As well as the Authorization and Nonce headers for allowing authentication.
	 *
	 * @since 5.5.0
	 * @since 6.3.0 The `$request` parameter was added.
	 *
	 * @param string[]        $allow_headers The list of request headers to allow.
	 * @param WP_REST_Request $request       The request in context.
	 */
	$allow_headers = apply_filters( 'rest_allowed_cors_headers', $allow_headers, $request );

	$this->send_header( 'Access-Control-Allow-Headers', implode( ', ', $allow_headers ) );

	$result = $this->check_authentication();

	if ( ! is_wp_error( $result ) ) {
		$result = $this->dispatch( $request );
	}

	// Normalize to either WP_Error or WP_REST_Response...
	$result = rest_ensure_response( $result );

	// ...then convert WP_Error across.
	if ( is_wp_error( $result ) ) {
		$result = $this->error_to_response( $result );
	}

	/**
	 * Filters the REST API response.
	 *
	 * Allows modification of the response before returning.
	 *
	 * @since 4.4.0
	 * @since 4.5.0 Applied to embedded responses.
	 *
	 * @param WP_HTTP_Response $result  Result to send to the client. Usually a `WP_REST_Response`.
	 * @param WP_REST_Server   $server  Server instance.
	 * @param WP_REST_Request  $request Request used to generate the response.
	 */
	$result = apply_filters( 'rest_post_dispatch', rest_ensure_response( $result ), $this, $request );

	// Wrap the response in an envelope if asked for.
	if ( isset( $_GET['_envelope'] ) ) {
		$embed  = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false;
		$result = $this->envelope_response( $result, $embed );
	}

	// Send extra data from response objects.
	$headers = $result->get_headers();
	$this->send_headers( $headers );

	$code = $result->get_status();
	$this->set_status( $code );

	/**
	 * Filters whether to send no-cache headers on a REST API request.
	 *
	 * @since 4.4.0
	 * @since 6.3.2 Moved the block to catch the filter added on rest_cookie_check_errors() from wp-includes/rest-api.php.
	 *
	 * @param bool $rest_send_nocache_headers Whether to send no-cache headers.
	 */
	$send_no_cache_headers = apply_filters( 'rest_send_nocache_headers', is_user_logged_in() );

	/*
	 * Send no-cache headers if $send_no_cache_headers is true,
	 * OR if the HTTP_X_HTTP_METHOD_OVERRIDE is used but resulted a 4xx response code.
	 */
	if ( $send_no_cache_headers || ( true === $method_overridden && str_starts_with( $code, '4' ) ) ) {
		foreach ( wp_get_nocache_headers() as $header => $header_value ) {
			if ( empty( $header_value ) ) {
				$this->remove_header( $header );
			} else {
				$this->send_header( $header, $header_value );
			}
		}
	}

	/**
	 * Filters whether the REST API request has already been served.
	 *
	 * Allow sending the request manually - by returning true, the API result
	 * will not be sent to the client.
	 *
	 * @since 4.4.0
	 *
	 * @param bool             $served  Whether the request has already been served.
	 *                                           Default false.
	 * @param WP_HTTP_Response $result  Result to send to the client. Usually a `WP_REST_Response`.
	 * @param WP_REST_Request  $request Request used to generate the response.
	 * @param WP_REST_Server   $server  Server instance.
	 */
	$served = apply_filters( 'rest_pre_serve_request', false, $result, $request, $this );

	if ( ! $served ) {
		if ( 'HEAD' === $request->get_method() ) {
			return null;
		}

		// Embed links inside the request.
		$embed  = isset( $_GET['_embed'] ) ? rest_parse_embed_param( $_GET['_embed'] ) : false;
		$result = $this->response_to_data( $result, $embed );

		/**
		 * Filters the REST API response.
		 *
		 * Allows modification of the response data after inserting
		 * embedded data (if any) and before echoing the response data.
		 *
		 * @since 4.8.1
		 *
		 * @param array            $result  Response data to send to the client.
		 * @param WP_REST_Server   $server  Server instance.
		 * @param WP_REST_Request  $request Request used to generate the response.
		 */
		$result = apply_filters( 'rest_pre_echo_response', $result, $this, $request );

		// The 204 response shouldn't have a body.
		if ( 204 === $code || null === $result ) {
			return null;
		}

		$result = wp_json_encode( $result, $this->get_json_encode_options( $request ) );

		$json_error_message = $this->get_json_last_error();

		if ( $json_error_message ) {
			$this->set_status( 500 );
			$json_error_obj = new WP_Error(
				'rest_encode_error',
				$json_error_message,
				array( 'status' => 500 )
			);

			$result = $this->error_to_response( $json_error_obj );
			$result = wp_json_encode( $result->data, $this->get_json_encode_options( $request ) );
		}

		if ( $jsonp_callback ) {
			// Prepend '/**/' to mitigate possible JSONP Flash attacks.
			// https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/
			echo '/**/' . $jsonp_callback . '(' . $result . ')';
		} else {
			echo $result;
		}
	}

	return null;
}