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.
$pathstringoptional
$_SERVER['PATH_INFO'] will be used.Default:null
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 '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;
}
Filters the list of request headers that are allowed for REST API CORS requests.
Filters whether the REST API is enabled.
Filters the list of response headers that are exposed to REST API CORS requests.
Filters whether JSONP is enabled for the REST API.
Filters the REST API response.
Filters the REST API response.
Filters whether the REST API request has already been served.
Filters whether to send no-cache headers on a REST API request.
| Version | Description |
|---|---|
| 4.4.0 | Introduced. |
© 2003–2024 WordPress Foundation
Licensed under the GNU GPLv2+ License.
https://developer.wordpress.org/reference/classes/wp_rest_server/serve_request