I am developing a plugin for a wordpress website which interacts with a third party HTTP API.
For one specific request I am completely dependent on a response header. It's a Location header used for redirection, from which I need to get the url and query parameters.
The request:
return wp_remote_post($url, array(
'body' => http_build_query(array(
'token' => <omitted>,
'email' => <omitted>,
'password' => <omitted>
)),
'headers' => array(
'Content-Type' => 'application/x-www-form-urlencoded'
),
'redirection' => 0
));
While testing the request above I noticed the headers response fields are null and {}:
{
"headers": {},
"body": "",
"response": {
"code": 303,
"message": "See Other"
},
"cookies": [
{
"name": "JSESSIONID",
"value": "<omitted>",
"expires": null,
"path": "/<omitted>",
"domain": "test.<omitted>.com",
"host_only": true
}
],
"filename": null,
"http_response": {
"data": null,
"headers": null,
"status": null
}
}
The request is working fine in Postman, where all the expected response headers are clearly visible. I also turned off following redirections in Postman for this specific request to be able to intercept the Location header before the redirection occurs.
I have tried:
another wordpress instance: same issue, no headers
a local docker wordpress instance: same issue, no headers
a different http request (GET https://example.com): same issue, no headers
setting redirection to 1 instead of 0: same issue, no headers, BUT the redirect actually works, which means the Location header is present and wp_remote_post picked it up - this makes it even harder to understand why I cannot see or retrieve any headers.
Here's the response when I GET https://example.com:
{
"headers": {},
"body": "<omitted>",
"response": {
"code": 200,
"message": "OK"
},
"cookies":[],
"filename": null,
"http_response": {
"data": null,
"headers": null,
"status": null
}
}
I have been searching for hours and I haven't gotten a step closer to resolving this issue. Any assistance is most welcome.
edit: someone asked me for the cURL PHP code from Postman:
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => '<omitted>/auth/login',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'token=<omitted>&email=<omitted>&password=<omitted>',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/x-www-form-urlencoded',
'Cookie: JSESSIONID=<omitted>'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
I worked around this issue by using another http library 'Requests'.
Wordpress usually includes this library by default.
$url = "$this->base_url/$this->base_path/auth/login";
$headers = array('Content-Type' => 'application/x-www-form-urlencoded');
$data = array(
'token' => <omitted>,
'email' => <omitted>,
'password' => <omitted>
);
$options = array(
'follow_redirects' => false
);
return Requests::post($url, $headers, $data, $options);
It does not solve the issue with wp_remote, but replaces it nicely for this specific request.
I am developing a mobile app (in Flutter) that needs to access course data of the user from a Wordpress LearnDash LMS environment. The Wordpress site is accessible via the standard REST API through the https://<site>/wp-json/ldlms/v1/ path.
But whenever I try to access e.g. <...>/ldlms/v1/sfwd-courses/<id>/steps, the result is a 401 (Unauthorized) status.
From the Wordpress documentation I learned it uses cookie authentication. But adding the wordpress_logged_in_<hash> cookie to the request header does not seem to make a difference.
Where can I find the missing authentication details required to access this information?
I just built my REST API; so I may be of some help. The example below will:
Register a custom rest api endpoint
Localize server-side variables on first load (endpoint, user object, etc)
Append the NONCE to all rest requests avoiding the hash BS
Rest API Authentication Reference
functions.php
add_action('rest_api_init', 'register_custom_fields');
function register_custom_fields(){
register_rest_route(
'app', '/login/',
[ 'methods' => 'POST', 'callback' => 'asset_login' ]
);
}
function asset_login(){
$user = wp_signon($_POST);
if (is_wp_error($user)){
return [ 'result' => 'fail', 'error' => $user->get_error_message() ];
} else {
return [ 'result' => 'success', 'user' => [ 'ID' => $user->data->ID, 'name' => $user->data->display_name ] ];
}
}
add_action('init','stage_script');
function stage_script(){
wp_enqueue_script('asset-library', trailingslashit(get_stylesheet_directory_uri()) . 'js/asset-library.js', ['jquery']);
$assetUser = null;
if (is_user_logged_in()){
$user = wp_get_current_user();
$assetUser = [
'ID' => $user->data->ID,
'name' => $user->data->display_name,
'nickname' => $user->data->user_nicename,
'email' => $user->data->user_email,
'allcaps' => $user->allcaps
];
}
wp_localize_script( 'your-app', 'App', [
'user' => $assetUser,
'api_endpoint' => esc_url_raw( rest_url() ),
'api_version' => 'wp/v2/',
'nonce' => wp_create_nonce( 'wp_rest' )
]);
}
script.js
// The Login Controller
let AppUser = {
loggedIn: false,
login: function(data){
$.ajax( {
url: App.api_endpoint + 'your-app/login',
method: 'POST',
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', App.nonce );
},
data: data,
success: function(response){
/* ... parse response ... */
},
error: function(response){
/* ... parse response ... */
}
});
}
};
// The API Controller
let QueryController = {
Objects: [],
getObjects: async function(){
let q = {};
/* ... build query stuff extracted */
try {
const response = await $.ajax( {
url: App.api_endpoint + App.api_version + 'post_object',
method: 'GET',
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', App.nonce );
},
data: q
});
this.Objects = response;
return response;
} catch(e) {
console.log('Error: ', e);
}
}
};
I am making Wordpress REST API on my custom plugin.
I have added this code on plugin construct
add_action('rest_api_init', function () {
register_rest_route( 'api/pay', 'transactions/(?P<category_id>\d+)',array(
'methods' => 'GET',
'callback' => 'get_latest_posts_by_category'
));
});
And made function like this.
function get_latest_posts_by_category($request) {
$args = array(
'category' => $request['transaction_id']
);
$response = new WP_REST_Response($args);
$response->set_status(200);
return $response;
}
But I can't call my rest api.
I have got this error:
{
"code": "rest_no_route",
"message": "No route was found matching the URL and request method",
"data": {
"status": 404
}
}
I get the next error:
Kreait\Firebase\Exception\Messaging\InvalidMessage
Client error: POST https://fcm.googleapis.com/v1/projects/wesig-c9298/messages:send resulted in a 400 Bad Request response:
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"status": "INVALID_ARGUMENT" (truncated...)
This is my code:
$serviceAccount = Firebase\ServiceAccount::fromJsonFile(__DIR__."/apikey.json");
$firebase = (new Firebase\Factory())
->withServiceAccount($serviceAccount)
->create();
$messaging = $firebase->getMessaging();
try {
$generator = new TokenGenerator('AAAALLHVwdc:APA91bFOI1eaWOQTlLq7oezE8E5yWqIb584xDSA10ylTYuIS4Ys9A0qQaqiBSp7A0jRT9_tsaCrNP59Wm-pzD_9wCO4uuxnwD1dmyc5_dF4i9iNHi1TLOxrwmvc-WMPG6K5YFYs5knvla2M_oV7WOKRToawWRNch3g');
$token = $generator
->setData(array('uid' => $user->attributes['uid']))
->create();
} catch (TokenException $e) {
echo "Error: ".$e->getMessage();
}
$notification = [
'title' => "Titulo",
'body' => "Cuerpo"
];
$data = [
'user' => 'yay',
'fecha' => date('d-m-Y')
];
$message = Firebase\Messaging\MessageToRegistrationToken::fromArray([
'token' => $token,
'notification' => $notification, // optional
'data' => $data, // optional
]);
$messaging->send($message);
I think that the error is within the Token, but I just can´t find a way to fix it.
From the documentation, it looks like TokenGenerator produces an auth token. That's not what you want here. The token in the message is the device registration token obtained from a call to a client-side SDK. For example, on Android, it's FirebaseInstanceId.getInstance().getInstanceId().
I'm trying to upload image via Wordpress REST api v2. So far all I managed was to create empty entries in wordpress media library. Meaning they have image names, but no actual image.
POST Request:
http://localhost/wordpress/wp-json/wp/v2/media
Authorization: Basic d29yZHByZXNzOndvcmRwcmVzcw==
Content-Type: application/json
Content-Disposition: attachment;filename=map2.jpg
{
"source_url" : "file:///C:/Users/x/Desktop/map2.jpg"
}
Response:
{
"id": 127,
"date": "2016-05-25T08:43:30",
"date_gmt": "2016-05-25T08:43:30",
"guid": {
"rendered": "http://localhost/wordpress/wp-content/uploads/2016/05/map2-3.jpg",
"raw": "http://localhost/wordpress/wp-content/uploads/2016/05/map2-3.jpg"
},
"modified": "2016-05-25T08:43:30",
"modified_gmt": "2016-05-25T08:43:30",
"password": "",
"slug": "map2-3",
"status": "inherit",
"type": "attachment",
"link": "http://localhost/wordpress/map2-3/",
"title": {
"raw": "map2-3",
"rendered": "map2-3"
},
"author": 1,
"comment_status": "open",
"ping_status": "closed",
"alt_text": "",
"caption": "",
"description": "",
"media_type": "image",
"mime_type": "image/jpeg",
"media_details": {},
"post": null,
"source_url": "http://localhost/wordpress/wp-content/uploads/2016/05/map2-3.jpg",
"_links": {
"self": [
{
"href": "http://localhost/wordpress/wp-json/wp/v2/media/127"
}
],
"collection": [
{
"href": "http://localhost/wordpress/wp-json/wp/v2/media"
}
],
"about": [
{
"href": "http://localhost/wordpress/wp-json/wp/v2/types/attachment"
}
],
"author": [
{
"embeddable": true,
"href": "http://localhost/wordpress/wp-json/wp/v2/users/1"
}
],
"replies": [
{
"embeddable": true,
"href": "http://localhost/wordpress/wp-json/wp/v2/comments?post=127"
}
]
}
}
I get no errors, everything's seem to be working, except response->post and response->media_details is either null or empty. Ofcourse image itself is not uploaded.
Based on this GitHub WP-API Adding Media ticket, I should send 2 requests. First POST request should return data with post object. I would send this post object via PUT method, and image should be uploaded...since I have no post object, this is not possible.
Any ideas what am I doing wrong?
Sideloading images is not supported by the wordpress api so you will have to do some changes.
First, your content-type should be image/jpeg and not application/json, remember that content-type is supposed to reflect the data that you are passing and the POST media request expects an image.
Another change you have to make to accommodate the content-type is the way that you are passing the data. Instead of sending it with the source_url parameter, try passing it as a binary file.
One last thing I would mention is that the wp/v2 calls return 3XX status on a few occasions. It would be useful to follow those redirects and redo those requests to those new urls.
I had some issues passing JPEG images but PNG images have worked well. Here is a curl example that I use to upload png media:
curl --request POST \
--url http://www.yoursite.com/wp-json/wp/v2/media \
--header "cache-control: no-cache" \
--header "content-disposition: attachment; filename=tmp" \
--header "authorization: Basic d29yZHByZXNzOndvcmRwcmVzcw==" \
--header "content-type: image/png" \
--data-binary "#/home/web/tmp.png" \
--location
My working answer using PHP cUrl
<?php
$curl = curl_init();
$data = file_get_contents('C:\test.png');
curl_setopt_array($curl, array(
CURLOPT_URL => "http://woo.dev/wp-json/wp/v2/media",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => array(
"authorization: Basic XxxxxxxXxxxxXx=",
"cache-control: no-cache",
"content-disposition: attachment; filename=test.png",
"content-type: image/png",
),
CURLOPT_POSTFIELDS => $data,
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
At january 2020 using wordpress 5.3.2 with nginx the solution that work for me is:
function uploadFile($token, $archivo) {
$file = file_get_contents( $archivo );
$mime = mime_content_type( $archivo );
$url = BASEAPI. 'wp-json/wp/v2/media';
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_POST, 1 );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt( $ch, CURLOPT_POSTFIELDS, $file );
curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
'Content-Type: '.$mime,
'Content-Disposition: attachment; filename="'.basename($archivo).'"',
'Authorization: Bearer ' .$token,
] );
$result = curl_exec( $ch );
curl_close( $ch );
print_r( json_decode( $result ) );
}
The token is the Authorization JWT token and $archivo is the path to file.
For anyone looking for a JS solution, here's how I made it work using Axios. I will skip authorization implementations, as there are a few options around (oAuth, JWT, Basic).
const fs = require('fs');
const axios = require('axios');
axios({
url: 'http(s)://{your-wp-domain}/wp-json/wp/v2/media',
method: 'POST',
headers: {
'Content-Disposition':'attachment; filename="file.jpg"',
Authorization: {your-authorization-method},
'Content-Type':'image/jpeg'
},
data: fs.readFileSync('path/to/file.jpg', (err, data) => {
if (err) {
console.log(err);
}
});
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});
Here you can use this code snippet. This works for me using Wordpress API REST
<?php
//Add this to your function.php
function upload_image( $imageID, $login ) {
$request_url = 'https://DOMAINNAME.com/wp-json/wp/v2/media'; //change the domainname
$image_file_path = get_attached_file($imageID); //change this to your file meda path if your not throwing media file to other server
$image = file_get_contents( $image_file_path );
$mime = mime_content_type( $image_file_path );
$api_media_response = wp_remote_post( $request_url, array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( $login ), //login format USERNAME:PASSWORD
'Content-Disposition' => 'attachment; filename='.basename($image_file_path).'',
'Content-Type' => $mime
),
'body' => $image
) );
//this function return wp_remote_post
// more info => https://developer.wordpress.org/reference/functions/wp_remote_post/
}
After trying to get the image upload running with wp_remote_post (don´t wanna use curl for several reasons) i came up with the following working solution:
// Upload image to wordpress media library
$file = #fopen( 'image.jpg', 'r' );
$file_size = filesize( 'image.jpg' );
$file_data = fread( $file, $file_size );
$args = array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( 'USERNAME:PASSWORD' ),
'accept' => 'application/json', // The API returns JSON
'content-type' => 'application/binary', // Set content type to binary
'Content-Disposition' => 'attachment; filename=nameoffileonserver.jpg'
),
'body' => $file_data
);
$api_response = wp_remote_post( 'http://myserver.com/wp-json/wp/v2/media', $args);
if you want to upload a image to WordPress rest API using nuxtjs or vuejs you can use the below code:
in template:
<input style="display: none;" type="file" #change="onFileSelected"
<button #click="onUpload" />
in data:
data() {
return {
selectedFile: null,
previewImage: null
};}
in methods:
onFileSelected(event) {
this.selectedFile = event.target.files[0];
}
onUpload() {
const fd = new FormData();
fd.append("file", this.selectedFile, this.selectedFile.name);
fd.append("title", "pedram");
fd.append("caption", "this is caption");
/* file reader for prview image */
const reader = new FileReader();
reader.readAsDataURL(this.selectedFile);
reader.onload = e =>{
this.previewImage = e.target.result;
};
/* set request header */
const headers = {
'Content-Disposition':`attachment; filename=${this.selectedFile.name}`,
Authorization: "Bearer" + this.$cookiz.get("AdminToken"),
'content-type': 'image'
};
this.$axios.post('/wp-json/wp/v2/media', fd, { headers })
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
},
if you want to preview the image you can use file reader and store it in a data variable then use the data instead of image src like bellow code:
<img #click="$refs.fileInput.click()" v-if="previewImage" :src="previewImage" alt="" class="w-100" />
It took me one day to solve the issue I hope the above codes helps