Community Documentation

Communicating with a remote API, implemented as a Services 3.0 REST Resource

Last updated February 24, 2011. Created by bsenftner on February 23, 2011.
Log in to edit this page.

The following demonstrates how to communicate with a remote API implemented as a Services 3.0  REST  resource using standard Drupal authentication (login/logout, user's account roles define user's permissions.) In a RESTful API, there is at least one "resource" defined, where each "resource" is something able to support operations in the CRUD pattern: Create, Retrieve, Update, Delete and Index (get a listing). Typically, a RESTful API implements a series of resources, each of which can have instances created, retrieved, updated and deleted. Aside from the necessity of logging in for authentication, most of what one can do within a RESTful API can be handled through one of five routines: Create(), Get(), Update(), Delete() and Index(). The code presented is a PHP object definition for managing communications with a remote API, primarily handled through routines supporting CRUD operations.

There are two example PHP objects defined here, each supporting the same logic, but for different environments. The first example demonstrates how to communicate with a remote API outside of Drupal (without the Drupal API); this is useful for remote API users who are not Drupal based. The second example demonstrates how to communicate with a remote API using Drupal's API. The Drupal API version uses fewer resources, which may matter to some people. And finally, some sample usage of the remote API communication objects is given. The logic is the same, regardless of using the non-Drupal version or the Drupal version.

Using CURL to communicate with a RESTful API

<?php
// *****************************************************************************************
// defines an object for working with a remote API, not using Drupal API
class RemoteAPI {
  public
$gateway;
  public
$apiVersion;
  public
$status;
  public
$session;    // the session name (obtained at login)
 
public $sessid;     // the session id (obtained at login)
 
 
const RemoteAPI_status_unconnected = 0;
  const
RemoteAPI_status_loggedin    = 1;
 
 
// *****************************************************************************
 
public function __construct( $gateway, $apiVersion ) {
   
$this->gateway    = $gateway;
   
$this->apiVersion = $apiVersion;
   
   
$this->status  = RemoteAPI_status_unconnected;
   
$this->session = '';
   
$this->sessid  = '';
  }

 
// *****************************************************************************
  // after login, the string generated here needs to be included in any http headers,
  // under the key 'Cookie':
 
private function GetCookieHeader() {
    return
$this->session.'='.$this->sessid;
  }
 
 
// *****************************************************************************
  // return the standard set of curl options for a POST
 
private function GetCurlPostOptions( $url, $data, $includeAuthCookie = false ) {
   
$ret = array( CURLOPT_URL => $url,
                 
CURLOPT_FAILONERROR => true,
                 
CURLOPT_RETURNTRANSFER => true,
                 
CURLOPT_TIMEOUT => 4,
                 
CURLOPT_HTTPHEADER => array('Accept: application/json'),
                 
CURLOPT_POST => true,
                 
CURLOPT_POSTFIELDS => $data,
                );
    if (
$includeAuthCookie) {
     
$ret[CURLOPT_COOKIE] = $this->GetCookieHeader();
    }
    return
$ret;
  }
   
 
// *****************************************************************************
  // return the standard set of curl options for a GET
 
private function GetCurlGetOptions( $url, $includeAuthCookie = false ) {
   
$ret = array( CURLOPT_URL => $url,
                 
CURLOPT_FAILONERROR => true,
                 
CURLOPT_RETURNTRANSFER => true,
                 
CURLOPT_BINARYTRANSFER => 1,
                 
CURLOPT_TIMEOUT => 3,
                 
CURLOPT_HTTPHEADER => array('Accept: application/json'),
                );
    if (
$includeAuthCookie) {
     
$ret[CURLOPT_COOKIE] = $this->GetCookieHeader();
    }
    return
$ret;
  }
   
 
// *****************************************************************************
  // return the standard set of curl options for a PUT
 
private function GetCurlPutOptions( $url, $data, $includeAuthCookie = false ) {
   
$ret = array( CURLOPT_URL => $url,
                 
CURLOPT_FAILONERROR => true,
                 
CURLOPT_RETURNTRANSFER => true,
                 
CURLOPT_TIMEOUT => 3,
                 
CURLOPT_CUSTOMREQUEST => 'PUT',
                 
CURLOPT_HTTPHEADER => array('Content-Length: ' . strlen($data),
                                             
'Accept: application/json'),
                 
CURLOPT_POSTFIELDS => $data,
                );
    if (
$includeAuthCookie) {
     
$ret[CURLOPT_COOKIE] = $this->GetCookieHeader();
    }
    return
$ret;
  }
   
 
// *****************************************************************************
  // return the standard set of curl options for a DELETE
 
private function GetCurlDeleteOptions( $url, $includeAuthCookie = false ) {
   
$ret = array( CURLOPT_URL => $url,
                 
CURLOPT_FAILONERROR => true,
                 
CURLOPT_RETURNTRANSFER => true,
                 
CURLOPT_TIMEOUT => 3,
                 
CURLOPT_HTTPHEADER => array('Accept: application/json'),
                 
CURLOPT_CUSTOMREQUEST => 'DELETE',
                );
    if (
$includeAuthCookie) {
     
$ret[CURLOPT_COOKIE] = $this->GetCookieHeader();
    }
    return
$ret;
  }
 
 
// *****************************************************************************
  // return false if we're logged in
 
private function VerifyUnconnected( $caller ) {
    if (
$this->status != RemoteAPI_status_unconnected) {
      return
false;
    }
    return
true;
  }
 
 
// *****************************************************************************
  // return false if we're not logged in
 
private function VerifyLoggedIn( $caller ) {
    if (
$this->status != RemoteAPI_status_loggedin) {
      return
false;
    }
    return
true;
  }
     
 
// *****************************************************************************
  // replace these 'resourceTypes' with the names of your resourceTypes
 
private function VerifyValidResourceType( $resourceType ) {
    switch (
$resourceType) {
      case
'project':
      case
'image':
      case
'thingy':
               return
true;
      default: return
false;
    }
  }
   
 
// *****************************************************************************
  // Perform the common logic for performing an HTTP request with cURL
  // return an object with 'response', 'error' and 'info' fields.
 
private function CurlHttpRequest( $caller, $url, $method, $data, $includeAuthCookie = false ) {
   
   
$ch = curl_init();    // create curl resource
   
switch ($method) {
      case
'POST':   curl_setopt_array($ch, $this->GetCurlPostOptions($url,$data, $includeAuthCookie)); break;
      case
'GET':    curl_setopt_array($ch, $this->GetCurlGetOptions($url, $includeAuthCookie));        break;
      case
'PUT':    curl_setopt_array($ch, $this->GetCurlPutOptions($url, $data, $includeAuthCookie)); break;
      case
'DELETE': curl_setopt_array($ch, $this->GetCurlDeleteOptions($url, $includeAuthCookie));     break;
      default:
        return
NULL;
    }
   
   
$ret = new stdClass;
   
$ret->response = curl_exec($ch); // execute and get response
   
$ret->error    = curl_error($ch);
   
$ret->info     = curl_getinfo($ch);
   
curl_close($ch);
   
    if (
$ret->info['http_code'] == 200) {
     
$ret->response = json_decode($ret->response);
    }
     
    return
$ret;
  }
 
 
// *****************************************************************************
  // Login: uses the cURL library to handle login
 
public function Login( $username, $password ) {
   
   
$callerId = 'RemoteAPI->Login';
    if (!
$this->VerifyUnconnected( $callerId )) {
      return
NULL; // error
   
}
   
   
$url = $this->gateway.$this->apiVersion.'/user/login';
   
$data = array( 'username' => $username, 'password' => $password, );
   
$data = http_build_query($data, '', '&');
   
$ret = $this->CurlHttpRequest($callerId, $url, 'POST', $data, false);
    if (
$ret->info['http_code'] != 200) {
      return
NULL;
    }
    else {
     
$this->sessid  = $ret->response->sessid;
     
$this->session = $ret->response->session_name;
     
$this->status = RemoteAPI_status_loggedin;
      return
true; // success!
   
}
 
  } 
// end of Login() definition
 
  // *****************************************************************************
  // Logout: uses the cURL library to handle logout
 
public function Logout() {
   
   
$callerId = 'RemoteAPI->Logout';
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
      
   
$url = $this->gateway.$this->apiVersion.'/user/logout';

   
$ret = $this->CurlHttpRequest($callerId, $url, 'POST', NULL, true);
    if (
$ret->info['http_code'] != 200) {
      return
NULL;
    }
    else {
     
$this->status = RemoteAPI_status_unconnected;
     
$this->sessid  = '';
     
$this->session = '';
      return
true; // success!
   
}
 
  } 
// end of Login() definition
 
  // **************************************************************************
  // perform an 'Index' operation on a resource type using cURL.
  // Return an array of resource descriptions, or NULL if an error occurs
 
public function Index( $resourceType ) {
   
   
$callerId = 'RemoteAPI->Index';
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
   
   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType;
   
$ret = $this->CurlHttpRequest($callerId, $url, 'GET', NULL, true);
    return
$ret->response;
  }

 
// *****************************************************************************
  // create a new resource of the named type given an array of data, using cURL
 
public function Create( $resourceType, $resourceData ) {
   
   
$callerId = 'RemoteAPI->Create: "'.$resourceType;
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
    if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }
   
   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType;
   
$data = http_build_query($resourceData, '', '&');
   
$ret = $this->CurlHttpRequest($callerId, $url, 'POST', $data, true);
    return
$ret->response;
  }
   
 
// **************************************************************************
  // perform a 'GET' operation on the named resource type and id using cURL.
 
public function Get( $resourceType, $resourceId ) {
   
   
$callerId = 'RemoteAPI->Get: "'.$resourceType.'/'.$resourceId.'"';
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
    if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }

   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType.'/'.$resourceId;
   
$ret = $this->CurlHttpRequest($callerId, $url, 'GET', NULL, true);
    return
$ret->response;
  }

 
// *****************************************************************************
  // update a resource given the resource type and updating array, using cURL.
 
public function Update( $resourceType, $resourceData ) {
   
   
$callerId = 'RemoteAPI->Update: "'.$resourceType;
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
     if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }
    if (!isset(
$resourceData['data']['id'])) {
      return
NULL;
    }
   
   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType.'/'.$resourceData['data']['id'];
   
$data = http_build_query($resourceData, '', '&');
   
$ret = $this->CurlHttpRequest($callerId, $url, 'PUT', $data, true);
    return
$ret->response;
  }   

 
// *****************************************************************************
  // perform a 'DELETE' operation on the named resource type and id using cURL
 
public function Delete( $resourceType, $resourceId ) {
   
   
$callerId = 'RemoteAPI->Delete: "'.$resourceType;
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
    if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }

   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType.'/'.$resourceId;
   
$ret = $this->CurlHttpRequest($callerId, $url, 'DELETE', NULL, true);
    return
$ret->response;
  } 
 
}
// end of RemoteAPI object definition using cURL and not Drupal API
?>

Using the Drupal API to communicate with a RESTful API

<?php
// *****************************************************************************************
// defines an object for working with the remote API, using the Drupal API
class RemoteAPI_via_DrupalAPI {
  public
$gateway;
  public
$apiVersion;
  public
$status;
  public
$session;    // the session name (obtained at login)
 
public $sessid;     // the session id (obtained at login)
 
 
const RemoteAPI_status_unconnected = 0;
  const
RemoteAPI_status_loggedin    = 1;
 
 
// *****************************************************************************
 
public function __construct( $gateway, $apiVersion ) {
   
$this->gateway    = $gateway;
   
$this->apiVersion = $apiVersion;
   
   
$this->status  = RemoteAPI_status_unconnected;
   
$this->session = '';
   
$this->sessid  = '';
  }
 
 
// *****************************************************************************
  // after login, the string generated here needs to be included in any http headers,
  // under the key 'Cookie':
 
private function GetCookieHeader() {
    return
$this->session.'='.$this->sessid;
  }
 
 
// *****************************************************************************
  // return false if we're logged in
 
private function VerifyUnconnected( $caller ) {
    if (
$this->status != RemoteAPI_status_unconnected) {
      return
false;
    }
    return
true;
  }
 
 
// *****************************************************************************
  // return false if we're not logged in
 
private function VerifyLoggedIn( $caller ) {
    if (
$this->status != RemoteAPI_status_loggedin) {
      return
false;
    }
    return
true;
  }
     
 
// *****************************************************************************
  // replace these with the resource type names you'll be using
 
private function VerifyValidResourceType( $resourceType ) {
    switch (
$resourceType) {
      case
'project':
      case
'image':
      case
'thingamajig':
               return
true;
      default: return
false;
    }
  }

 
// *****************************************************************************
  // Perform the common logic for performing an HTTP request with the Drupal API
 
public function DrupalHttpRequest( $caller, $url, $method, $data, $includeAuthCookie = false ) {

   
$headers = array();
   
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
    if (
$includeAuthCookie) {
     
$headers['Cookie']     = $this->GetCookieHeader();
    }
   
    if (
$data) {
     
$data = http_build_query($data, '', '&');
    }
   
   
$response = drupal_http_request($url, $headers, $method, $data);
    if (
$response->code == 200) {
     
$response->data = json_decode($response->data);
    }
    else {
     
$response->data = NULL;
    }
    return
$response;
  }
 
 
// *****************************************************************************
 
public function Login( $username, $password ) {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Login';
    if (!
$this->VerifyUnconnected( $callerId )) {
      return
NULL; // error
   
}
   
   
$url = $this->gateway.$this->apiVersion.'/user/login';
   
$data = array( 'username' => $username, 'password' => $password, );
   
   
$response = $this->DrupalHttpRequest($callerId$url, 'POST', $data, false);
    if (
$response->code == 200) {
     
$this->session = $response->data->session_name;
     
$this->sessid  = $response->data->sessid;
     
$this->status  = RemoteAPI_status_loggedin;
      return
true; // meaning okay
   
}
   
    return
NULL; // meaning error
 
}

 
// *****************************************************************************
 
public function Logout() {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Logout';
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
   
   
$url = $this->gateway.$this->apiVersion.'/user/logout';
   
   
$response = $this->DrupalHttpRequest($callerId, $url, 'POST', NULL, true);
    if (
$response->code == 200) {
     
$this->status = RemoteAPI_status_unconnected;
     
$this->sessid  = '';
     
$this->session = '';
      return
true; // success!
   
}
   
    return
NULL;
  } 

 
// **************************************************************************
  // perform an 'Index' operation on a resource type using Drupal API.
  // Return an array of resource descriptions, or NULL if an error occurs
 
public function Index( $resourceType ) {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Index';
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
   
   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType;
   
$response = $this->DrupalHttpRequest($callerId$url, 'GET', NULL, true);
    return
$response->data; // if failed, this is NULL, if success, this is an object holding requested data
 
}
 
 
// *****************************************************************************
  // create a new resource of the named type given an array of data, using Drupal API
 
public function Create( $resourceType, $resourceData ) {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Create:'.$resourceType;
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
    if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }
   
   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType;
   
$response = $this->DrupalHttpRequest($callerId, $url, 'POST', $resourceData, true);
    return
$response->data; // if failed, this is NULL, if success, this is an object holding response data
 
}
   
 
// **************************************************************************
  // perform a 'GET' operation on the named resource type and id using Drupal API
 
public function Get( $resourceType, $resourceId ) {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Get:'.$resourceType.'/'.$resourceId.'"';
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
    if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }

   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType.'/'.$resourceId;
   
   
$response = $this->DrupalHttpRequest($callerId$url, 'GET', NULL, true);

    return
$response->data; // if failed, this is NULL, if success, this is an object holding response data
 
}

 
// *****************************************************************************
  // update a resource given the resource type and updating array, using Drupal API
 
public function Update( $resourceType, $resourceData ) {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Update:'.$resourceType;
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
     if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }
    if (!isset(
$resourceData['data']['id'])) {
     
_devReport('missing referencing ID of update resource!');
      return
NULL;
    }
   
   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType.'/'.$resourceData['data']['id'];
   
$response = $this->DrupalHttpRequest($callerId, $url, 'PUT', $resourceData, true);
    return
$response->data; // if failed, this is NULL, if success, this is an object holding response data
 
}   

 
// *****************************************************************************
  // perform a 'DELETE' operation on the named resource type and id using Drupal API
 
public function Delete( $resourceType, $resourceId ) {
   
   
$callerId = 'RemoteAPI_DrupalAPI->Delete:'.$resourceType;
    if (!
$this->VerifyLoggedIn( $callerId )) {
      return
NULL; // error
   
}
    if (!
$this->VerifyValidResourceType($resourceType)) {
      return
NULL;
    }

   
$url = $this->gateway.$this->apiVersion.'/'.$resourceType.'/'.$resourceId;
   
$response = $this->DrupalHttpRequest($callerId$url, 'DELETE', NULL, true);
    return
$response->data; // if failed, this is NULL, if success, this is an object holding response data
 

 
}
// end of RemoteAPI_via_DrupalAPI object definition
?>

Example usage of communicating with a remote API

<?php
 
// create a new API communications object like this:
  // the two parameters compose the URL the API communications take place
  // (they are separate to facilitate API gateways and different API versions)
 
$apiObj = new RemoteAPI_DrupalAPI( 'http://api-project-clientName.apigee.com', // gateway
                                    
'/clientName/api/dev'   // Services end point
                                     
);
 
 
// here's the logic to login. Required for authentication, and all other operations:
 
$apiObj->Login( 'joeuser', 'XXXXXXX' ); // pass in username and password
 
  // if a resource type called "project" existed, a new project could be created like this:
 
 
$data = array( 'data' => array( 'title'    => 'ApiDefinedProject',  
                                 
'status'   => 'active',                 // fantasy fields
                                 
'body'     => 'demo information',      
                                 
'comments' => 'disabled',              
                                 
'tags'     => '',                      
                                ),
              );
 
$ret = $apiObj->Create('project', $data );
 
// $ret will be NULL if an error occured, or will be an object with an 'id' field
 
  // retrieve our new project:
 
$ret = $apiObj->Get('project', $ret->id);  // <-  'id' used to get new project

  // modifying the data for a modify:
 
$data['data']['body'] .= " can be modified too";
 
$data['data']['id'] = $ret->id// <- so Update() knows what to modify
 
$ret = $apiObj->Update('project', $data );

 
// and delete just needs the id:
 
$ret = $apiObj->Delete('project', $ret->id );

 
// when completed working with the API, logout like this:
 
$apiObj->Logout();
?>

Comments

I couldn't get this working with Drupal 7

Hello,

I just tried this against a Drupal 7 install and I get 200 as a response code but nothing in the response piece of the array so I get no session id returned:

Class Object
(
[response] =>
[error] =>
[info] => Array
(
[url] => http://drupalwebsvcstest.babson.edu/drupal7/user/login
[content_type] => text/html; charset=utf-8
[http_code] => 200
[header_size] => 435
[request_size] => 202
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0.140793
[namelookup_time] => 0.000237
[connect_time] => 0.000328
[pretransfer_time] => 0.00033
[size_upload] => 34
[size_download] => 8689
[speed_download] => 61714
[speed_upload] => 241
[download_content_length] => -1
[upload_content_length] => 0
[starttransfer_time] => 0.127042
[redirect_time] => 0
[certinfo] => Array
(
)

)

)

Are we sure this works with Drupal 7 ? I really wish the Oauth module worked with Drupal 7. Any help on this would be great. Thanks.

This was written and tested

This was written and tested in Services 3.0 for Drupal 6 - but the essential logic is the same.

You say you're getting a 200 response code - which means it is working somewhere, you're just not getting your session data back. Are you in control the the site with Services 3.0 installed? You should be able to add debugging logic to see where your data is being dropped...

(You did visit the Services configuration pages and enable the "user" resource's login and logout APIs for use, right?)

Thanks I got this code working with Drupal 7

same as what you were doing, but it was easier for me to understand my own code I wrote, I can put it in classes later. thanks for your posts. I guess curl can do a lot more than I knew.

<?php
$ch
= curl_init();
curl_setopt($ch, CURLOPT_URL,"http://drupal_base_url/drupal7/user/login");
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP script');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, "cookie.txt");
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "name=username&pass=password&form_id=user_login");
curl_exec ($ch);

curl_setopt($ch, CURLOPT_URL,"http://drupal_base_url/test_endpoint/users");
curl_setopt($ch, CURLOPT_HTTPGET, 1);
curl_exec ($ch);

curl_close ($ch);
unset(
$ch);
?>

See my blog for a full post of my code which returns just json which is more useful: http://www.barnettech.com/drupal7_services_curl_rest_api

http://www.barnettech.com/content/drupal-7-create-user-using-rest-and-curl

<?php
$ch
= curl_init();
curl_setopt($ch, CURLOPT_URL,"http://base_url/user/login");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP script');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, "cookie.txt");
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "name=username&pass=password&form_id=user_login");
ob_start();  //dont print out this output
curl_exec ($ch);

curl_setopt($ch, CURLOPT_URL,"http://base_url/test_endpoint/users");
//curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json'));
//curl_setopt($ch, CURLOPT_HTTPGET, 1);
ob_end_clean();  //resume printout output to screen

/*if ($ret->info['http_code'] == 200) {
      $ret->response = json_decode($ret->response);
    }*/

//print_r($ret);
curl_setopt($ch, CURLOPT_POST, 1);
//curl_setopt($ch, CURLOPT_FAILONERROR, true);
//curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$resourceData = array( 'account' => array('username' => 'jbarnett',  
                     
'name' => 'jbarnett',
                                         
'pass' => 'xxxxxx',                
                                         
'mail' => 'username@yahoo.com',                   
                                         ),
                     );

$data2 = http_build_query($resourceData, '', '&');


curl_setopt($ch, CURLOPT_POSTFIELDS, "account=".$data2);

$ret = new stdClass;
$ret->response = curl_exec($ch); // execute and get response
$ret->error    = curl_error($ch);
$ret->info     = curl_getinfo($ch);

/*if ($ret->info['http_code'] == 200) {
      $ret->response = json_decode($ret->response);
    }*/
print $ret->response;
print
$ret->error;



curl_close ($ch);
unset(
$ch);
?>

One thing: it throws away the first element 'username' in this array, hence I just put in a bogus element -- username -- , as you know name is the element name in fact (I'm still looking at why it's doing this):

$resourceData = array( 'account' => array('username' => 'jbarnett',
'name' => 'jbarnett',
'pass' => 'xxxxxx',
'mail' => 'username@yahoo.com',
),
);

I'm seeing the same thing for update statements. The first element of the array is thrown away.

I had to put this in to make it work:

$resourceData = array( 'throwaway' => array('mail' => 'test'),
'data' => array(
'mail' => 'testmail@yahoo.com',
),
);

$data2 = http_build_query($resourceData, '', '&');

Then I ran curl_exec (with all the right options for curl_setop)
I created the user but is this a bug with the services module that it always throws up the first element of the array?
My full code which successfully updates a user using rest, services and curl is here: http://www.barnettech.com/content/drupal-7-update-user-using-rest-curl-a...

I noticed strange behavior

I noticed strange behavior with the data parameters myself, and came to the conclusion that the post data needed a 'double wrapper'. If you look at my code example of usage (from above, but duplicated here:)

<?php
  $data
= array( 'data' => array( 'title'    => 'ApiDefinedProject'
                                 
'status'   => 'active',                 // fantasy fields
                                 
'body'     => 'demo information',     
                                 
'comments' => 'disabled',             
                                 
'tags'     => '',                     
                                ),
              );
?>

Notice how the data is actually in $data['data'] rather than just in $data? I found this was necessary to get my parameters into my logic on the other side of Services.

Note that this issue should be brought up in the Services issue queue. I think it's a matter of documentation - simply let us know what is the right way do pass in data. Right now, only the module itself and old examples are available.

Thanks so much for your great articles,it is really very useful for me .I'm using your drupal Api to post a comment to node 2,
my test code is :
$apiObj = new RemoteAPI_via_DrupalAPI( 'http://localhost:8888', '/fhxmt/rest' );
$apiObj->Login( 'username, 'password' ); // pass in username and password
$data = array("data"=>array(
"nid" => "2",
"comment_body" =>
array(
"und" => array(
"0" => array( "value" => "555 anonymous need to be approved? ")
)
)
)
);

$ret = $apiObj->Create('comment.create', $data );
$apiObj->Logout();

I can Login and Logout successfully. But I can't post the comment ,the response is alway something like :
"HTTP/1.0 401 Unauthorized: Missing required argument comment" ,I think the format of my $data array must be wrong.
Could you please give me some guidelines ?

ps.
I can use
$data = array(
"nid" => "2",
"comment_body" =>
array(
"und" => array(
"0" => array( "value" => "555 anonymous need to be approved? ")
)
)
);
to post the comments successfully in xmlrpc service.

zhou bin

Sorry; I have not

Sorry; I have not experimented with standard Drupal comments, so I don't have any useful advice. Due to the lack of documentation, and the lack of step-by-step debugging of PHP, I use the Devel Module's dsm() and dpm() routines, sprinkled through the Services Module's source code to painfully and "time consumingly" figure it out.

Drupal comments themselves are 'non-standard' enough that I've not bothered to figure them out for programmatic operations. I'll need to soon, so an update may occur to this documentation after that. (I need to programmatically update comments by the end of September, 2011.)

Should this be added to your great document?

I forget something, in order to get Login and Logout work ,I Have to add
$headers['Accept'] = 'application/json';
to the DrupalHttpRequest method, should this be added to your great document?

zhou bin

Which version of Services are

Which version of Services are you running? I've been using the above code, pretty much unaltered, for months. I just noticed a new Release Candidate for Services 3.0 is out, and perhaps if you're on that and I'm not yet, that's a key difference?

True, if you have not enabled x-www-form-urlencoded

If you haven't enabled application/x-www-form-urlencoded request parsing in the services server definition page, you need to do this. You'll also need to json_encode() the value you use for CURLOPT_POSTFIELDS.

Sample code eats server response in error conditions

One problem with this code, if the server produces an error, the HTTP response body is tossed out. Typically when dealing with a RESTful server the response body contains some interesting info on the error, possibly to show the user.

You'll need to change CURLOPT_FAILONERROR to false if you want the response body to come back when your call produces an error.

Page status

Needs technical review

Log in to edit this page

About this page

Drupal version
Drupal 6.x, Drupal 7.x
Audience
Developers and coders

Develop for Drupal

Drupal’s online documentation is © 2000-2012 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License. Comments on documentation pages are used to improve content and then deleted.
nobody click here