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.
it's my first time to create rest api with custom endpoint in wordpress the api is working fine but the problem is i want to add key and value in the header when i send request to the api , to avoid using the api in postman without adding these key and value in the header
here is the code that create the api :
add_action( 'rest_api_init', 'wl_posts');
function wl_posts(){
register_rest_route('w1/v1','fav/',[
'methods' => 'POST',
'callback' => 'test',
'header' => 'username : test'
]);
any help ?
You can create a function from you callback that gathers the param you need, and do what you need after:
<?php
function callback_name( $data ) {
$your_var = $data->get_param( 'param_name' );
// Do other stuff that you need to do
}
?>
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.
I know it's a little bit complicated but there's a lot of benefits for me using this scenario,
I'm working on a WordPress project and need to verify the user's license so I made an AJAX form has the required fields from user, Unfortunately the verification API needs a Secret Key (Belongs to me and cannot include in the client's project) so I made an external WordPress website (Subfolder on my website) and develop two small [Plugin & Theme].
- In the client side
I prepare the user sensitive data to send to my website using WordPress function (wp_safe_remote_get) like this...
$request_url = 'HTTPS://MY_WEBSITE/verify?api=XXX&some=XXX&some=XXX';
$response = wp_safe_remote_get(
$request_url,
array(
'timeout' => 300
)
);
- On my Website
The theme has an [3 files: style.css, functions.php and verify.php] and the verify.php has this line [ do_action( 'xxx' ) ].
The plugin has [ add_action( 'xxx' ) ] in the constructor to do the verification process and of course I included the My SECRET KEY here ..
Collect $_GET params and verify then return an array with the result using [print_r] so the final result exactly like this from browser Inspect Element
body_tag>
Array (
[API_RESULT] => Array ( 'verified' => true );
)
/body>
The Issue
In the client side I received the previous array BUT besides a lot of included scripts and css paths belongs to my WordPress website, So...
I need to receive only the response body without the other included files.
Is that secure enough?
Is there a solution for this scenario something like create a PHP file rather than a WordPress site but will be more secure??
Thanks for your help.
1. Server Side
If you need to send an array to the client side, Do your processes you need then print serialized array.
index.php
do_action( 'YOUR_ACTION_NAME', 'VAR_1', 'VAR_2' );
class_processes.php
function __construct() {
add_action( 'YOUR_ACTION_NAME', array( &$this, '__Trigger_User_Action' ), 10, 2 );
}
public function __Trigger_User_Action( $seller_name = '', $token_type ) {
$user_final_data = array();
// Do Some
die( serialize( $user_final_data ) );
}
2. Client Side
$request_url = 'https://DOMAIN.XXX/user_processes?user_name=XXX&code=XXX';
$response = wp_safe_remote_get(
$request_url,
array(
'timeout' => 300
)
);
if( ! is_wp_error( $response ) ) {
$body_data = wp_remote_retrieve_body( $response );
if( ! is_wp_error( $body_data ) ) {
$user_server_data = #unserialize( $body_data );
}
}
Now $user_server_data contains the the array you passed in [print] function through your server.
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";
}