Handles serving a REST API request.
Description
Matches the current server URI to a route and runs the first matching callback then outputs a JSON representation of the returned value.
See also
Parameters
$path
stringoptional- The request route. If not set,
$_SERVER['PATH_INFO']
will be used.
Default:
null
Source
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;
}
Hooks
- apply_filters( ‘rest_allowed_cors_headers’,
string[] $allow_headers ,WP_REST_Request $request ) Filters the list of request headers that are allowed for REST API CORS requests.
- apply_filters_deprecated( ‘rest_enabled’,
bool $rest_enabled ) Filters whether the REST API is enabled.
- apply_filters( ‘rest_exposed_cors_headers’,
string[] $expose_headers ,WP_REST_Request $request ) Filters the list of response headers that are exposed to REST API CORS requests.
- apply_filters( ‘rest_jsonp_enabled’,
bool $jsonp_enabled ) Filters whether JSONP is enabled for the REST API.
- apply_filters( ‘rest_post_dispatch’,
WP_HTTP_Response $result ,WP_REST_Server $server ,WP_REST_Request $request ) Filters the REST API response.
- apply_filters( ‘rest_pre_echo_response’,
array $result ,WP_REST_Server $server ,WP_REST_Request $request ) Filters the REST API response.
- apply_filters( ‘rest_pre_serve_request’,
bool $served ,WP_HTTP_Response $result ,WP_REST_Request $request ,WP_REST_Server $server ) Filters whether the REST API request has already been served.
- apply_filters( ‘rest_send_nocache_headers’,
bool $rest_send_nocache_headers ) Filters whether to send no-cache headers on a REST API request.
Changelog
Version | Description |
---|---|
4.4.0 | Introduced. |
User Contributed Notes
You must log in before being able to contribute a note or feedback.