Wordpress REST API Caching Issue - WP Engine - php

I have created a custom plugin that actually just registeres a few API endpoints.
Right now the issue I'm having is that all of the endpoints are working fine locally, but when I push this code to WpEngine where I've hosted my WordPress site the API responses are getting cached.
If I clear the cache through WPEngine and make the request again the API is working fine until a 200 success response is received for the first time, once success is received then from the point the endpoint is always returning the same response no matter what header, parameter value I give to that endpoint.
In the wp-config.php file I've disabled the cache - define( 'WP_CACHE', false );
also tried adding
wp_cache_flush();
nocache_headers();
in the request action call back functions too, still no success, always the response are cached.
Few Code snippets for your reference -
// This is the route I've registered
public function register_routes()
{
register_rest_route($namespace, '/config' ,[
'methods' => 'GET',
'callback' => array($this, 'Action_GetConfig'),
'permission_callback' => 'authCheck'
]);
}
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
function Action_GetConfig(WP_REST_Request $request)
{
try {
// wp_cache_flush();
// nocache_headers();
$headers = $request->get_headers();
// Basic Validation
if (IsNullOrEmptyString($headers['platform'][0]) or IsNullOrEmptyString($headers['version'][0])) {
$resp = new WP_HTTP_Response();
// $resp->set_headers( array('Cache-Control' => 'no-cache, must-revalidate, max-age=0'));
$resp->set_status(400);
return $resp;
}
// Service Invocation
$results = $this->getConfig($headers['platform'][0], $headers['version'][0]);
$resp = new WP_HTTP_Response($results);
$resp->set_status(200);
return $resp;
} catch (Throwable $e) {
//$log->error($e);
$resp = new WP_HTTP_Response($e);
$resp->set_status(500);
return $resp;
}
}
Can someone help in solving this API response caching issue? Thanks!

I believe WP ENGINE USES, WP Engine MU PLUGIN. If so with this plugin you will have access to a few functions called
wpecommon::purge_varnish_cache()
, if you pass the ID of a particular post that you were targeted for, this function will clear the post cache. But if you use the function without passing the ID then it will clear all the cache of the entire domain( I would certainly not recommend this way, as it will cause a performance issue on a large site).
function your_call_of_action(WP_REST_Request $request){
$id = $request->get_param( 'id' );
if ( FALSE === get_post_status( $id ) ) {
$resp = new WP_HTTP_Response([]);
$resp->set_status(500);
return $resp;
}
wpecommon::purge_varnish_cache( $id )
// rest of your code goes here
}
Also, you can call the following API to clear the cache :
/installs/{install_id}/purge_cache
Read the following documents:
Read this document: https://wpengine.com/support/cache/
API Document: https://wpengineapi.com/reference

I solved this issue by adding a Cache Exclusion Policy in WPEngine. Because by default in WPEngine all the Endpoints responses are cached. So if we want NOT to cache a specific route, then we have to add that route to the Exclusion list.
path: ^/wp-json/nx/services/?
I added this above Route RegEx in WPEngine.

Related

Communicating between two WordPress site using REST API & without any authentication

I have 2 WordPress sites in 2 different domains. One is my server & another one is my client site. In my server WordPress, I have a custom WordPress REST API endpoint that accepts POST requests. The client will send some custom data to the server. Then the server processes the data & returns an output.
The issue is that I can't use any authentication method here. The API must be public & any WordPress site can send some data to the server. In the server's REST API callback function, I have added some custom sanitization & validation. Also, nothing to be saved in the DATABASE.
I have added the below code snippet to make a public API endpoint in my server.
add_action( 'rest_api_init', 'register_rest_route_abcd' );
function register_rest_route_abcd() {
register_rest_route(
'mycustomapi', 'v1/abcd',
array(
'methods' => 'POST',
'callback' => array( 'my_callback_function'),
'permission_callback' => '__return_true',
)
);
}
Then in the client file, I have used wp_safe_remote_post to post my custom data.
wp_safe_remote_post( $target_url, array('body' => $request_data) )
The requested data is custom data & there are no authentication parameters there. I got the error given below.
{"code":"rest_not_logged_in","message":"You are not currently logged in.","data":{"status":401}}
I need a public API endpoint where anyone can submit some data.
NOTE: I have a custom string like a key in the submitted data & can I use that string to authenticate API response?
Update: The issue was automatically resolved. I haven't done any changes in the code or file.
I have attached the code snippet that will be useful for someone else. Public API that accepts POST requests is always vulnerable. So be careful. I believe that later we can get some suggestions regarding it.
In server-side
add_action( 'rest_api_init', 'register_rest_route_abcd');
function register_rest_route_abcd() {
register_rest_route(
'myapi', 'v1/myroute',
array(
'methods' => 'POST,GET',
'callback' => 'my_callback_function',
'permission_callback' => '__return_true',
)
);
}
function my_callback_function($request){
//Do logic here
// Prepare response
$response = array();
$response['status'] = true;
$response['message'] = 'done';
return $response;
}
In clint side
add_action( 'template_redirect', 'ed45r_my_action', 5 );
function ed45r_my_action(){
$target_url = 'http://localhost/server-sys/wp-json/myapi/v1/myroute';
$data = array(
'key_1' => 'value_1',
'key_2' => 'value_2',
);
$request = wp_safe_remote_get( $target_url, array('body' => $data) );
if(is_wp_error($request) || wp_remote_retrieve_response_code($request) != 200){
// Know the errror
} else {
$response = wp_remote_retrieve_body( $request );
$response = json_decode($response, true);
// Process the response
}
}
In my case, the action is not a template_redirect. It is a form submission.

Wordpress Ajax and REST API response shows data with cached result WP Engine

I have created custom REST API and in response i am getting old data result. I am using WP Engine and cache is enabled in the wp-config.php file. I don't want to disable cache, just want my api respone without cached data result. I tried to set header Cache-Control: no-cache, must-revalidate, max-age=0, ob_flush(), clearstatcache() but still having same issue. Also, in my ajax request when i return html data, it shows me old entries. I cross checked in database, it have the correct values but response of ajax and api request are the previous one. When i clear cache from the WP Engine settings on Dashboard then i am getting correct result in ajax and api response.
Here is my code api code
register_rest_route( PLUGIN_DIRNAME.'/v1', '/data/status', array(
'methods' => 'GET',
'callback' => array( $this, 'get_status' )
));
public function get_status($request){
$response = array();
$progress = get_option('progress_percentage');
$name = get_option('form_submission');
$response["progress"] = $progress;
$response["form_id"] = $name;
if($progress == '100'){
$response["submission_status"] = __( 'successfull');
}
$result = new WP_REST_Response( $response, 200);
$result->set_headers( array(
'Cache-Control' => 'no-cache, must-revalidate, max-age=0'
) );
return $result;
}
In ajax response :
function ajax_response(){
$progress = get_option('progress_percentage');
if($progress == "100"){
$name = get_option('form_submission');
$submission_status = "<div class='complete'>Form submission successfull.</div>";
}
return array(
'message' => get_option('submission_stage'),
'html' => $submission_status,
);
}
Can anyone help me with this issue. I am struggling with this issue from 4 hours. It would be great help if anyone suggestion made my code working.
Thanks in Advance.
Finally i managed to get this issue fixed. I am posting here if in case anyone face the similar issue. I used wp_cache_flush() in the begining of api callback function and in the ajax request callback function. It fixed the cache issue for me.

How do I get response from object sent via a request with wordpress api

I have registered a api request the following way in the code, then in postman I call that request and add some params, but when I run the api request endpoint it returns null.
How do I return the data that's being sent?
/**
* This is our callback
* function that embeds our phrase in a WP_REST_Response
*/
function addProductFromCRM($data) {
//$name = $data['name'];
// rest_ensure_response() wraps the data we want to return into a WP_REST_Response, and ensures it will be properly returned.
return rest_ensure_response($data);
}
/**
* This function is where we register our routes for our example endpoint.
*/
function wp_register_crm_routes() {
// register_rest_route() handles more arguments but we are going to stick to the basics for now.
register_rest_route('crm/v1', '/addproduct/', array(
// By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
'methods' => 'POST',
// Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
'callback' => 'addProductFromCRM',
));
}
add_action('rest_api_init', 'wp_register_crm_routes');
What addproduct endpoint should return? JSON? You can do something like this:
function addProductFromCRM($request) {
wp_send_json($request->get_params());
}
You can use below code snippet as per you need like on isset($_POST) or any other callback function. You must have the idea of your Register Route URL and must be working. you can use wp_remote_get or wp_remote_post as per your need. for more reference please check WordPress official site
$response = wp_remote_get("URL TO YOUR REGISTER ROUTE");
if ( is_array( $response ) ) {
$response_code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
$body_data = json_decode($body);
}

WordPress API Permission Callback – Check if user is logged in

I have a question about permission callbacks when working with the WP Rest API. I have registered a couple of endpoints using register_rest_route and simply want to secure them so that you can’t access the content if you’re not logged in.
add_action('rest_api_init', 'register_custom_endpoints');
function register_custom_endpoints(){
register_rest_route($base, $endpoint, [
'methods' => 'POST',
'callback' => function($request){
// Return JSON data
},
'permission_callback' => function($request){
// This always returns false
return is_user_logged_in();
},
]);
}
I think my logic here is correct but is_user_logged_in() always returns false, meaning I’ll never get the data even if I’m actually logged in. All I get is a 404 response:
{code: "rest_user_invalid_id", message: "Invalid user ID.", data: {status: 404}}
I’ve been scouring the web for answers and what I’ve found is that I need to send a nonce with the request. I’ve tried sending it both as body data and as a header. If I send it in the body, I can’t verify it as it always returns false. And if I send it as a header, I get the same 404 response as stated above.
What am I missing here, and what am I doing wrong?
I recently encountered the same issue and it turns out that you need to create a nonce and pass it with your request headers.
You create the appropriate nonce with the following php code.
$nonce = wp_create_nonce( 'wp_rest' );
Then you pass that nonce with the HTTP request via the header X-WP-Nonce.
Assuming your client is on the same domain (ergo has WordPress's auth cookies set) your session should be accessible via the REST API so functions like current_user_can and is_user_logged_in work as they would outside the REST API.
A simple way to solve this is instead of permission_callback parameter that check user logged in status in the request handler function with a GET request URL parameter. You can add logged in user ID or a temporary generated nonce / hash in the URL and check on server side that they are the same or not, for example: domain.tld/wp-json/user/1/todo/
class UserAPI
{
public function __construct()
{
// do nothing
}
public function setHooks()
{
add_action('rest_api_init', [$this, 'actionRestApiInit']);
}
public function actionRestApiInit()
{
// add current user ID into GLOBALS to get it in the request handler function
$GLOBALS['user_id'] = get_current_user_id();
register_rest_route(
'user',
'(?P<userID>\d+)/(?P<action>[a-zA-Z0-9-]+)/',
[
'methods' => 'GET',
'callback' => [$this, 'runAction'],
]
);
}
public function runAction(\WP_REST_Request $data=null)
{
$params = $data->get_params();
if ((int)$params['userID']!==(int)$GLOBALS['user_id']) {
return new \WP_Error( 'rest_forbidden', __('Sorry, you are not allowed to do that.'), ['status' => 401] );
}
wp_send_json(['Logged in, run api'], 200);
}
}
(new UserAPI())->setHooks();
Or as a rest route attribute:
register_rest_route(
'user',
'(?P<userID>\d+)/(?P<action>[a-zA-Z0-9-]+)/',
[
'methods' => 'GET',
'callback' => [$this, 'userActions'],
'user_ID' => get_current_user_id(),
]
);
and than:
if ( (int) $params['userID'] !== (int) $data->get_attributes()['user_ID'] )
you can use current_user_can( 'edit_posts' );
it will only allow to admin user it work with me
for more info go to
https://developer.wordpress.org/reference/functions/current_user_can/

How to get current logged in user using Wordpress Rest Api?

I tried to add a custom request.
add_action('rest_api_init', function () {
register_rest_route( 'custom', '/login', array(
'methods' => 'GET',
'callback' => function(WP_REST_Request $request) {
return wp_get_current_user();
}
));
});
But it always returns a user with with ID = 0;
I also tried this:
add_action('rest_api_init', function () {
register_rest_route( 'custom', '/login', array(
'methods' => 'GET',
'callback' => function(WP_REST_Request $request) {
return is_user_logged_in();
}
));
});
And it always returns false.
But the user is logged in for sure.
I added my custom login
add_action('rest_api_init', function () {
register_rest_route( 'custom', '/login', array(
'methods' => 'POST',
'callback' => function(WP_REST_Request $request) {
$nonce = wp_create_nonce("wp_rest");
$user = wp_signon(array('user_login' => $_POST['username'],
'user_password' => $_POST['password'], "rememberme" => true), false);
if (is_wp_error($user)) {
return $user;
}
//do_action( 'wp_login', "capad" );
//$user['isloggedin'] = is_user_logged_in();
return array('user' => $user,
'nonce' => $nonce);
}
));
});
And I add "X-WP-Nonce" in as a header for http request
And now every request outputs: {"code":"rest_cookie_invalid_nonce","message":"Cookie nonce is invalid","data":{"status":403}}
From the Authentication chapter, in the REST API Handbook:
Cookie authentication is the basic authentication method included with
WordPress. When you log in to your dashboard, this sets up the cookies
correctly for you, so plugin and theme developers need only to have a
logged-in user.
However, the REST API includes a technique called nonces to avoid CSRF
issues. This prevents other sites from forcing you to perform actions
without explicitly intending to do so. This requires slightly special
handling for the API.
For developers using the built-in Javascript API, this is handled
automatically for you. This is the recommended way to use the API for
plugins and themes. Custom data models can extend wp.api.models.Base
to ensure this is sent correctly for any custom requests.
For developers making manual Ajax requests, the nonce will need to be
passed with each request. The API uses nonces with the action set to
wp_rest. These can then be passed to the API via the _wpnonce data
parameter (either POST data or in the query for GET requests), or via
the X-WP-Nonce header.
Here's a GET example:
https://example.tld/wp-json/wp/v2/users/me?_wpnonce=9467a0bf9c
or in your case:
https://example.tld/wp-json/custom/login/?_wpnonce=9463a0bf9c
where the nonce is created from
wp_create_nonce( 'wp_rest' );
So most likely you forgot about the nonce part when testing your custom endpoint.
Hope it helps!
I spent two days searching for a simple way without adding plugins.
first in function.php where you define your api
//enqueue the script which will use the api
function api_callings_scripts() {
wp_enqueue_script('score-script', get_template_directory_uri() . '/js/ScoreSaving.js', ['jquery'], NULL, TRUE);
// Pass nonce to JS.
wp_localize_script('score-script', 'ScoreSettings', [
'nonce' => wp_create_nonce('wp_rest'),
]);
}
add_action( 'wp_enqueue_scripts', 'api_callings_scripts' );
Then your script Ajax call cloud be something like this
jQuery.ajax({
type: "POST",
url: "/wp-json/score/update",
data: {"var1":"value1"},
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', ScoreSettings.nonce);
},
success:
function( data ) {
console.log( data );
}
});
Now you can use get_current_user_id() inside your API code.
1. Install and activate JWT Authentication for WP REST API plugin, also install WP REST API plugin
2. Now you can run any wordpress default api from mobile app or any other source or by postman. for example hit this url from your app or by postman. https://example.com/wp-json/wp/v2/posts
3. By app or by postman, When you will login with valid details (using rest api) you will get back a token. To login and get token, run the following url by postman or by app
https://example.com/wp-json/jwt-auth/v1/token
4. By this way you will get a token as shown in picture
Now use this token to get logged in user details, for example
5. make function in function.php
function checkloggedinuser()
{
$currentuserid_fromjwt = get_current_user_id();
print_r($currentuserid_fromjwt);
exit;
}
add_action('rest_api_init', function ()
{
register_rest_route( 'testone', 'loggedinuser',array(
'methods' => 'POST',
'callback' => 'checkloggedinuser'
));
});
6. Now again run this new url in postman or in app to get logged in user details. https://example.com/wp-json/testone/loggedinuser (replace example.com with your url)
(https://i.stack.imgur.com/tIqhS.png)
7. Also edit your .htaccess file and wp-config.php file according to instructions on pt.wordpress.org/plugins/jwt-authentication-for-wp-rest-api
Here is another way
$user_id = ""; //<- add this
add_action( 'rest_api_init', 'add_custom_users_api');
function add_custom_users_api(){
$GLOBALS['user_id'] = get_current_user_id(); //<- add this
// route url: domain.com/wp-json/mmw/v1/testing
register_rest_route( 'mmw/v1', 'testing', array(
'methods' => 'GET',
'callback' => 'get_custom_users_data',
));
}
//Customize the callback to your liking
function get_custom_users_data(){
return get_user_by( 'id', $GLOBALS['user_id'] ); //<- add this
}
If you prefer use JWT Authentication for WP REST API, it may be easier to implement with Json Web Tokens.
First you authenticate the client sending a HTTP POST request to the endpoint /wp-json/jwt-auth/v1/token sending username and password fields to generate a auth token.
A succefull response would be similar to:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9qd3QuZGV2IiwiaWF0IjoxNDM4NTcxMDUwLCJuYmYiOjE0Mzg1NzEwNTAsImV4cCI6MTQzOTE3NTg1MCwiZGF0YSI6eyJ1c2VyIjp7ImlkIjoiMSJ9fX0.YNe6AyWW4B7ZwfFE5wJ0O6qQ8QFcYizimDmBy6hCH_8",
"user_display_name": "admin",
"user_email": "admin#localhost.dev",
"user_nicename": "admin"
}
Then you pass the token each request settings the request header Authorization like:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9qd3QuZGV2IiwiaWF0IjoxNDM4NTcxMDUwLCJuYmYiOjE0Mzg1NzEwNTAsImV4cCI6MTQzOTE3NTg1MCwiZGF0YSI6eyJ1c2VyIjp7ImlkIjoiMSJ9fX0.YNe6AyWW4B7ZwfFE5wJ0O6qQ8QFcYizimDmBy6hCH_8
I was dealing with the same issue where get_current_user_id() would return 0. After some testing I found this was only the case when sending POST requests.
With GET requests the function returned the logged in user ID, on a POST request it returned 0. This could be due to a server configuration perhaps as this was on a shared hosting environment. I did not find any posts or comments mentioning this before, but this is what happened in my case. A simple workaround could be to pass the user ID back to the frontend in a GET request, store it and send it on POST requests as an extra param.
I think it one of the most flexible way to retrieve the user id in rest callback
<?php
// register route
add_action( 'rest_api_init', function () {
register_rest_route( 'el-dashboard-api', '/generic-comments', [
'methods' => 'GET',
'callback' => 'get_generic_comments',
'login_user_id' => get_current_user_id(), // This will be pass to the rest API callback
]);
});
// rest callback
function get_generic_comments($REST_REQUEST_OBJ){
// get all the passed attrs
$attrs = $REST_REQUEST_OBJ->get_attributes();
// check is login_user_id set as attr
if( isset($attrs['login_user_id']) && intval($attrs['login_user_id']) > 0 ){
$user_id = intval($attrs['login_user_id']);
$current_user = get_user_by( 'id', $user_id );
return "Authorize";
}
return "Not authorize";
}

Categories