Try Tuts+ Premium, Get Cash Back!
preview

Working with RESTful Services in CodeIgniter

CodeIgniter is becoming well known for its power as a PHP based web application framework, but it’s not often that we see examples of it being used for anything else. Today we’ll learn how we can use CodeIgniter to create a RESTful API for your existing web applications, and demonstrate how to interact with your own API or other RESTful web-services, such as Facebook and Twitter.

Tutorial Details

Introduction

If you have been following the CodeIgniter From Scratch series you will know by now that it is relatively quick and easy to put together simple web applications, such as blogs, CMS systems, brochure sites, etc. One thing you may not have thought about is using CodeIgniter to create an interactive API. After trying several existing REST implementations, I found they not only lacked simplicity but were missing most of the features you would expect from a RESTful implementation; so I built my own. This tutorial will show you how to use this code to set up your REST API, and gives example of how to interact with it from your web application.

Assumptions

  1. You have a web server set up, locally or online and known how to manage files on it.
  2. You have read a few of the CodeIgniter from Scratch tutorials.
  3. You know how to set up CodeIgniter.
  4. You know a little about RESTful services.

This tutorial is broken down into two parts. We will start by learning how to create a RESTful service, then further down, we will learn how to interact with it in a few different ways.

Part 1 – Creating a RESTful API

Step 1: Setting up the Demo

Firstly you need to download the codeigniter-restserver code from GitHub and extract it and move the code to your server.

When you open the folder, you will see an entire CodeIgniter install, which is there to power the demo. This allows people to have a play with the REST demo before integrating with your existing application.

Open up “application/config/config.php” and set the base_url to get links working. This base_url will be different for everyone and depends entirely on where you uploaded your files.

Step 2: The URLs

With the files extracted and the base_url set, we are ready to load up our RESTful CodeIgniter installation, and have a look at the demo supplied with it. Browse the base URL, which by default is:

http://localhost/restserver

Here you will find a few example links to the example_api controller, which can be found at “application/controllers/example_api.php”. Let’s dissect the URL’s of these examples to see what is going on. The first URL is a very simple one.

This URL looks very much like any other CodeIgniter URL with a controller and a method, but you will notice in this diagram that the method is named a “Resource”. REST is all about Resources and they are essentially a noun within your application, which are interacted with (i.e added, deleted, edited, queried) based on HTTP headers and URL query strings or HTTP arguments.

The default format for output is XML which is what we see in this basic example. The other links are slightly larger and demonstrate how to pass parameters and show how the output format can be modified in the URL:

Normally in CodeIgniter, you just pass in parameter values, but a REST controller accepts any number of parameters in any order. For this to work, we need to pass in the name of the parameter followed by the value in pairs.

At the end of the URL is the “format” parameter. This is a reserved parameter that will modify the output format of the requested data like so:

By giving both the API developer and the client application the choice of data formats to use, the API is opened up to a much wider audience and can be used with more programming languages and systems. These three are not the only formats supported, out of the box your REST API can use:

  • xml – almost any programming language can read XML
  • json – useful for JavaScript and increasingly PHP apps.
  • csv – open with spreadsheet programs
  • html – a simple HTML table
  • php – Representation of PHP code that can be eval()’ed
  • serialize – Serialized data that can be unserialized in PHP

While adding the format to the URL is not technically the most RESTful way to change formats, it allows for easy browser testing and lets developers without cURL perform simple GET requests on the API. The more RESTful way is to send a Content-type HTTP header to the REST controller using cURL, but that will be explained later.

Step 3: The Code

Now if you open up application/controllers/example_api.php you will immediatley spot a few differences from normal CodeIgniter controllers.

REST_Controller

In the MVC pattern, a controller is the central point of the logic. It is called when a user makes a request and then based on the logic in the controller it fetches data and outputs views. CodeIgniter contains its own logic for how a Controller should work, but as we are doing something different we need our own REST_Controller library to contain its own REST related logic. So instead of simply using:

<?php
class Example_api extends Controller {

}

…you will need to use:

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

}

Working with Resources

Now your empty controller is set up, next are the methods or “resources”. This is prossibly the most confusing part of the tutorial if you are used to how CodeIgniter works. Basically, you take the Resource and the HTTP verb and combine them to make a method name. So the two examples we looked at before had a Resource of user and users. Because both of these were loaded in the browser, we know it was using a GET request and so the two methods below are used:

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

    function user_get()
    {
		// respond with information about a user
    }
    
    function users_get()
    {
		// respond with information about several users
    }
}

This may seem a little strange, but it gives you the ability to use the same URL and respond to the request depending on the HTTP verb that has been used. If somebody tries to access your API in a way that is not allowed (in this example PUT or DELETE) it will simply respond with a 404. If you aren’t sure about HTTP verbs, let me explain.

GET

Used to fetch information about an existing resource. This is used by browsers when you enter a URL and hit go, or when you click on a link, so it perfect for fetching information on one of your REST resources (like user).

POST

Used to update an existing resource with information. Browsers use this to submit most types of forms on the internet, although some use GET as well by submitting the form action with a query string containing the field data.

PUT

Less commonly used and not supported by most browsers, PUT is used to create a new resource.

DELETE

Also not used by many browsers, this HTTP verb rather obviously is used to delete a resource.

If we put that into code and allow each verb on the resource user it would look like this:

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

    function user_get()
    {
		// respond with information about a user
    }

    function user_put()
    {
		// create a new user and respond with a status/errors
    }

    function user_post()
    {
		// update an existing user and respond with a status/errors
    }

    function user_delete()
    {
		// delete a user and respond with a status/errors
    }
}

Accessing parameters and returning data

So now the API has been given its structure by setting up the resources and defining a method for each HTTP verb we wish to support; we need parameters so we can use our CodeIgniter models and libraries. This is one of the major benefits of using CodeIgniter for our API, as we can use our existing models and libraries and not have to re-code them.

<?php
require(APPPATH'.libraries/REST_Controller.php');

class Example_api extends REST_Controller {

    function user_get()
    {
		$data = array('returned: '. $this->get('id'));
		$this->response($data);
    }
    
    function user_post()
    {		
		$data = array('returned: '. $this->post('id'));
		$this->response($data);
    }

    function user_put()
    {		
		$data = array('returned: '. $this->put('id'));
		$this->response($data;
    }

    function user_delete()
    {
		$data = array('returned: '. $this->delete('id'));
		$this->response($data);
    }
}

This example contains five new pieces of code:

$this->get()

Is used to return GET variables from either a query string like this index.php/example_api/user?id=1 or can be set in the more CodeIgniter’esque way with index.php/example_api/user/id/1.

$this->post()

Is an alias for $this->input->post() which is CodeIgniter’s method to access $_POST variables with XSS protection.

$this->put()

Reads in PUT arguments set in the HTTP headers or via cURL.

$this->delete()

You guessed it, this reads in DELETE arguments, also set in HTTP headers or via cURL.

$this->response()

Sends data to the browser in whichever data format has been requested, or defaults to XML. You can optionally pass a HTTP status code to show it has worked or failed. E.g if the ID provided was not in the database, you could use $this->response(array(‘error’ => ‘User not found.’), 404);

Step 4: Working with your Models

Until now, we have been working with an example API in a clean install. So the next step is to get a REST API running from your existing codebase.

Although the download comes with a full CodeIgniter installation for the demo and to allow API’s to be built from scratch, the only two files of importance are:

  1. application/config/rest.php
  2. application/libraries/REST_Controller.php

Drop those two files into your CodeIgniter application and create a new API controller.

<?php
require(APPPATH.'/libraries/REST_Controller.php');

class Api extends REST_Controller
{
	function user_get()
    {
        if(!$this->get('id'))
        {
        	$this->response(NULL, 400);
        }

        $user = $this->user_model->get( $this->get('id') );
    	
        if($user)
        {
            $this->response($user, 200); // 200 being the HTTP response code
        }

        else
        {
            $this->response(NULL, 404);
        }
    }
    
    function user_post()
    {
        $result = $this->user_model->update( $this->post('id'), array(
        	'name' => $this->post('name'),
        	'email' => $this->post('email')
        ));
        
        if($result === FALSE)
        {
        	$this->response(array('status' => 'failed'));
        }
        
        else
        {
        	$this->response(array('status' => 'success'));
        }
        
    }
    
    function users_get()
    {
        $users = $this->user_model->get_all();
        
        if($users)
        {
            $this->response($users, 200);
        }

        else
        {
            $this->response(NULL, 404);
        }
    }
}
?>

This shows an example API with some generic model names. In the first method, we are picking up a ?id=XX and passing it to the model. If data is found we send it to the $this->response() function with a status 200. If nothing is found, return no body and a 404 to say nothing was found. You can imagine how this could be expanded to run all sorts of API activities for your web application.

Step 5: Securing the API

Now your API is built it needs securing so only users given access can interact with the API. To set the login type, usernames and passwords open up “application/config/rest.php” inside your codebase.

/*
|--------------------------------------------------------------------------
| REST Login
|--------------------------------------------------------------------------
|
| Is login required and if so, which type of login?
|
|	'' = no login required, 'basic' = relatively secure login, 'digest' = secure login
|
*/
$config['rest_auth'] = 'basic';

None

Anyone can interact with any one of of your API controllers.

Basic

A relatively insecure login method which should only be used on internal/secure networks.

Digest

A much more secure login method which encrypts usernames and password. If you wish to have a protected API which anyone could get at, use digest.

/*
|--------------------------------------------------------------------------
| REST Login usernames
|--------------------------------------------------------------------------
|
| Array of usernames and passwords for login
|
|	array('admin' => '1234')
|
*/
$config['rest_valid_logins'] = array('admin' => '1234');

Setting up the users is simple. Each login is an array item, with a key and a value. The key is the username and the value is the password. Add as many as you like to this array and dish them out to anyone who will be using the API.

Part 2 – Interacting with RESTful Services

Whether it is the API you have just built or a public service such as Twitter, you will want to be able to interact with it somehow. Seeing as RESTful services work with basic HTTP requests it is very easy to do this in a number of different ways.

Different Methods to Interact with REST

Each of these different interaction methods will be shown with the code placed directly in the Controller methods. This is purely so the demos are easier to read and would normally would be placed inside a model or a library for correct MVC separation.

file_get_contents()

Using the very simple PHP function file_get_contents(), you can perform a basic GET request. This is the most basic of all the methods but is worth mentioning for those “quick and dirty” moments.

$user = json_decode(
	file_get_contents('http://example.com/index.php/api/user/id/1/format/json')
);

echo $user->name;

It’s worth noting that, while this method will not work using HTTP Digest authentication, if you are using HTTP Basic authentication you can use the following syntax to get data from your password protected RESTful API:

$user = json_decode(
	file_get_contents('http://admin:1234@example.com/index.php/api/user/id/1/format/json')
);

echo $user->name;

There are a few problems with using this method: the only way to set extra HTTP headers is to set them manually using the PHP function stream_context_create(), which can be very complicated for developers who are new to the internal workings of HTTP requests. Another disadvantage is that you only receive the body of the HTTP response in its raw format, which means you need to handle conversion from very single request.

cURL

cURL is the most flexible way to interact with a REST API as it was designed for exactly this sort of thing. You can set HTTP headers, HTTP parameters and plenty more. Here is an example of how to update a user with our example_api and cURL to make a POST request:


    function native_curl($new_name, $new_email)
    {
	    $username = 'admin';
		$password = '1234';
		
		// Alternative JSON version
		// $url = 'http://twitter.com/statuses/update.json';
		// Set up and execute the curl process
		$curl_handle = curl_init();
		curl_setopt($curl_handle, CURLOPT_URL, 'http://localhost/restserver/index.php/example_api/user/id/1/format/json');
		curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl_handle, CURLOPT_POST, 1);
		curl_setopt($curl_handle, CURLOPT_POSTFIELDS, array(
			'name' => $new_name,
			'email' => $new_email
		));
		
		// Optional, delete this line if your API is open
		curl_setopt($curl_handle, CURLOPT_USERPWD, $username . ':' . $password);
		
		$buffer = curl_exec($curl_handle);
		curl_close($curl_handle);
		
		$result = json_decode($buffer);

		if(isset($result->status) && $result->status == 'success')
		{
			echo 'User has been updated.';
		}
		
		else
		{
			echo 'Something has gone wrong';
		}
    }

Interacting with your API this way works fine, but there are two problems with this method:

  1. It uses an ugly confusing syntax – imagine creating several an application based on that.
  2. cURL is not installed on all servers by default.

To solve this ugly syntax, a cURL library has been developed for CodeIgniter which simplifies things immensely.

The exact same request made with the cURL library would look like this:

    function ci_curl($new_name, $new_email)
    {
	    $username = 'admin';
		$password = '1234';
		
		$this->load->library('curl');
		
		$this->curl->create('http://localhost/restserver/index.php/example_api/user/id/1/format/json');
		
		// Optional, delete this line if your API is open
		$this->curl->http_login($username, $password);

		$this->curl->post(array(
			'name' => $new_name,
			'email' => $new_email
		));
		
		$result = json_decode($this->curl->execute());

		if(isset($result->status) && $result->status == 'success')
		{
			echo 'User has been updated.';
		}
		
		else
		{
			echo 'Something has gone wrong';
		}
    }

Much nicer to look at right? Well there is an even easier method to work with REST in your CodeIgniter applications that this.

REST client library

A REST client library has been developed that sits on top of this cURL library which handles format conversion, HTTP logins and several other aspects of your REST API.

    function rest_client_example($id)
    {
		$this->load->library('rest', array(
			'server' => 'http://localhost/restserver/index.php/example_api/',
			'http_user' => 'admin',
			'http_pass' => '1234',
			'http_auth' => 'basic' // or 'digest'
		));
		
        $user = $this->rest->get('user', array('id' => $id), 'json');
        
        echo $user->name;
    }

Here you can see we are making a GET request, sending id as a parameter and telling the library we want ‘json’ as the content format. This handles the setting of Content-type for you, and converts the data into a PHP object for you. You can change this value to ‘xml’, ‘json’, ‘serialize’, ‘php’, ‘csv’ or any custom MIME-type you like, for example:

	$user = $this->rest->get('user', array('id' => $id), 'application/json');

As you have probably guessed as well as $this->rest->get(), the library also supports $this->rest->post(), $this->rest->put(), $this->rest->delete() to match all of your REST_Controller methods.

You will need to var_dump() results coming from the REST client library to make sure you are getting the right data format back. The conversion will somtimes be array and sometimes be an object, depending on how it is converted by PHP. If the returned MIME-type is not supported then it will simply return the format as plain-text.

Talking to Twitter

Using this REST library you can talk other RESTful services such as Twitter and Facebook. Here is a simple example of how you can get details for a specfic user based on their ID, using Twitter’s default format XML.

        $this->load->library('rest', array('server' => 'http://twitter.com/'));
		
        $user = $this->rest->get('users/show', array('screen_name' => 'philsturgeon'));
        $this->load->library('rest', array(
        	'server' => 'http://twitter.com/',
			'http_user' => 'username',
			'http_pass' => 'password',
			'http_auth' => 'basic'
        ));
		
        $user = $this->rest->post('statuses/update.json', array('status' => 'Using the REST client to do stuff'));

Looking at this, you will notice that interacting with the Twitter API is a bit different in a few ways.

  1. They support URL based format switching in the form of .json instead of /format/json. Some require an extension, some do not; so it’s best to always add them.
  2. They mostly only support GET/POST, but are starting to add more DELETE methods
  3. They don’t always have just a resource in their URL, for example: users/search is one REST method, but lists is another.

Keep an eye out for these differences as they can catch you out. If you get stuck, simply echo $this->rest->debug() for a whole range of information on your REST request.

Summary

Combining what you now know about RESTful services, the CodeIgniter REST client library and the Twitter API documentation – or any other RESTful API documentation for that matter – you can create some very powerful applications that integrate with any custom or public web service using REST. You can extend your API by creating more REST_Controller’s and even make a modular API by using Matchbox or Modular Separation to create an api.php controller for each module to help keep your API as neatly organized as your application.

Write a Plus Tutorial

Did you know that you can earn up to $600 for writing a PLUS tutorial and/or screencast for us? We’re looking for in depth and well-written tutorials on HTML, CSS, PHP, and JavaScript. If you’re of the ability, please contact Jeffrey at nettuts@tutsplus.com.

Please note that actual compensation will be dependent upon the quality of the final tutorial and screencast.

Write a PLUS tutorial

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • raypath

    I am looking example ready of auth in this API REST use existing DB application where in table users exist username/password (stored with md5) instead existing auth method in API REST basic and digest

  • raypath

    I have problem with xml from this API. The one of data from array which is passed to function _format_xml() in REST_Controler.php is look above but in xml tag is empty.

    ‘desc’ => ‘Strzelno to niewielkie miasteczko leżące w powiecie mogileńskim. Jedna z najstarszych miejscowości Polski. Ośrodek turystyczny z cennymi zabytkami romańskimi.
    Nie sposób przedstawić tu wszystkich zabytków tego miasta, pokrótce wskażę jedynie na te najważniejsze:

    Romański Kościół Św. Trójcy zawierający ponad trzysta skatalogowanych zabytków.
    Kolumny w kościele św. Trójcy, obok Drzwi Gnieźnieńskich najcenniejszy zabytek sztuki romańskiej w Polsce.
    Czarny ołtarz Krzyża Świętego umieszczony w północnej częsci transeptu Bazyliki z słynącą cudami figurą Chrystusa Ukrzyżowanego z 1361 roku. Cały ołtarz jest relikwiarzem, znajduje się w nim 658 relikiwi. Nigdzie w kraju nie ma tylu relikwi w jednym miejscu. W całym Krakowie znajduje się trochę ponad tysiąc relikwii, w Strzelnie w dwóch kosciałach jest ich około tysiąca.
    Zespół pięciu konfesjonałów z 1741 roku, z unikalnymi malowidłami z polskimi napisami sprzed 250 lat.
    Rotunda Św. Prokopa – jeden z najlepiej zachowanych kościołów romańskich w Polsce, z najstarszą w kraju mającą około 500 lat drogą krzyżową.
    Wczesnośredniowieczny tympanon fundacyjny – znajduje się nad wejściem do kaplicy św. Barbary.

    Miasteczko niesamowite warto tam pojechać i wszystko to i jeszcze wiele innych zabytków zobaczyć na własne oczy. Warto skorzystać z usług przewodnika.
    Więcej o Strzelnie można poczytać tutaj: http://www.strzelno.pl/?a=1038
    Podczas wyjazdu do Strzelna ku swojemu wielkiemu zdziwieniu stwierdziłem, że jak do tej pory miasteczko to jeszcze nie ma swojego kesza. Teraz juz ma!!!
    Kesz to mikromagnetyk, nie zawiera ołówka. Skrzynka umieszczona jest na terenie muzealno-kościelnym i nie wiem czy aby wieczorem obszar ten nie jest zamykany, w każdym razie w ciągu dnia można kesza podjąć bez najmniejszego problemu i opłat za bilety wstępu.
    Miłego zwiedzania i szukania!!!
    ‘,

  • raypath

    Ups, In may post above problem with xml is after update REST_Controler.php where function _format_xml() don’t exist now. It is look xml generate by core CI ?

  • raypath

    OK problem with xml I have solved.

  • raypath

    Ok I have see that I can use API-Key to get access users.

    Users can generate/update own API-Key in own profile and I can use API-Key.

    I have question what is level in API-Key ?

    I will be nice to more configure use API-Key for example only for GET it is not mandatory use API-Key only for PUT /DELET but when user use API-Key in GET it will be use to map username

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      “level” is a super-basic permission system, where a key and a method both have a “level”.

      If a method is protected by 10 and a key has only level 1 then the request will error. If the key had 10 or 11 then it would work.

      These numbers are entirely arbitrary and optional.

      • http://www.google.com/ Kamryn

        That’s the best answer by far! Thanks for cotnrbuiting.

  • luis83rs

    Hello,

    This is great for mobile and I’ve just discovered it, great job!

    I have one simple question about the rest models:

    If I make the call by using $this->rest->db->whatever It won’t work

    But If I use $this->db->whatever it will work

    Am I missing something?

    An another question, in the rest config file the parameter:

    $config['rest_enable_keys'] = FALSE;

    How does it work, if I change it to TRUE it always give me “Ivalid Api Key”. Even If I tried to pass a valid key in a hidden input field by post.

    Thank you for your contribution :)

  • Marcos Filho

    Very good Tuto :) This library could come in CI by default .

  • CDesaulniers

    Hello Phil, this tuto and controllers are great stuff, but there is something weird.

    I’m trying to test my server functions without having to rely on rest_client by using Poster, a firefox extension to test restful services. Something is acting weird, i’m trying to test a POST function but for some reason, CI is not receiving the headers and/or parameters (tested both just in case), aka $_POST or $this->post seems to be empty

    I also tried another extension, “Client Rest” which seems to produce the same exact things…

    Although if i produce manual code with firephp + your rest client it seems to work flawlessly.

    Is there a setting i should be aware of? Thanks in advance

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      If you want to send POST using tools like this then you need to do what the browser normally does without you noticing.

      the body should contain a query string (no “?”) and the Content-Type header needs to be application/x-www-form-urlencoded.

      • dsjellz

        So does this mean that the only supported post/put data format is application/x-www-form-urlencoded? Post data seems to come through fine w/ that content-type but not application/json.

      • http://swaggable.com Faisal

        You are a live saver!

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      Of course you can post other bodies, but if you want to populate $_POST then this is how you do it.

      Other data is sent to $this->rest->body.

      • James

        $this->rest->body does not pickup my JSON-object that I send via POST.

        {“email”:”example@example.com”}

        How can I detect it and will CI be able to “validate” it?

  • http://www.tenthweb.com/ Damu

    I am getting: “Not authorized” when using REST.

    i can access fine like this: http://api.test.dev/investor/investor/user/id/1/method/json

    But when i access with rest as:

    function rest_client_example($id)
    {
    $this->load->library(‘rest’, array(
    ‘server’ => base_url(),
    ‘http_user’ => ‘admin’,
    ‘http_pass’ => ’1234′,
    ‘http_auth’ => ‘basic’ // or ‘digest’
    ));

    $user = $this->rest->get(‘investor/investor/user’, array(‘id’ => $id), ‘json’);

    echo $user->name;
    }

    var_dump($user);

    //gives
    object(stdClass)#16 (2) { ["status"]=> bool(false) ["error"]=> string(14) “Not authorized” }

  • Olix

    I deploy on a machine with the following config:

    $config['index_page'] = “index.php?”;
    $config['uri_protocol'] = “QUERY_STRING”;

    but calling the
    http://domain.com/index.php?controller/id/10
    will render the “$this -> get(id)” false.

    any comments?

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      By setting index to index.php? you are setting the query string to /controller/whatever so of course $this->get() wont work. There is only one query string and you are trying to use two!

  • Artem

    Is there a reason why REST_Controller is in libraries folder and not in controllers? I mean, it IS a controller. Let me know, thanks.

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      The REST_Controller class is no more a controller than CI_Controller and so is treated the. In a perfect world it would be a abstract class but this lead to a few inheritance issues.

  • Rob Claisse

    Thanks so much for providing this code as its a massive help to get REST functionality into CI – but I have come a cross an issue which I’m struggling to solve.

    Basically I’ve used the HMVC extensions (as part of Bonfire) and the REST server does work, in some ways, as it sets the headers and returns the correct XML but I also get an error which invalidates the XML:

    A PHP Error was encountered
    Severity: Notice
    Message: Undefined property: CI::$security
    Filename: MX/Loader.php
    Line Number: 166

    Its seems like its an issue with HMVC more than Bonfire but any pointers towards how to find a solution would be graetfully received!

  • Onema

    Great resource, I’m considering adding it to our CI app. Any way of integrating this with oAuth to authenticating clients?

  • m4k

    Hi,
    Thanks for awesome liberary. In my app I want to have user authentication via table how do I achieve that? And every time user access the api it should see Wether user has authenticated.

  • Neeraj Avinash

    A small gentle note.

    I was having problem with digest authentication using CURL. I made some tweak and override the method of the request by assigning
    curl_setopt($curl_handle, CURLOPT_POSTFIELDS, array(
    ‘_method’ => ‘put’,
    ‘id’ => $id,
    ));

    What I am doing is I am always doing a post through curl and with ‘_method’ I am overriding the default method. The rest controller detects the request method so in the example above it will detect request method as PUT and is assigned to $this->request->method as put.

    In the function _prepare_digest_auth at line no 734
    $A2 = md5(strtoupper($this->request->method) . ‘:’ . $digest['uri']);

    It will supply put as the request and not post while curl posted the data. It will fail to authenticate the user.
    After I modified this to be
    $A2 = md5($this->input->server(‘REQUEST_METHOD’) . ‘:’ . $digest['uri']); it all worked well.
    Now I have all authentication methods working.
    Thanks.

  • http://peppitools.com venkatareddy Bhavanam

    Its working fine but facing one problem i.e if i have to send more than one variable? for example if i want to send user information with $user and some other info with some other variable.how to send multiple variables as response?

  • Nick

    Hey Phil,
    I came across this tutorial as I was browsing for CI API libraries. This looks great. I was running an old version of CI (1.7.2), so I upgraded to 2.0.2. I’m running HVMC, so I upgraded that to the most recent version as well so that everything would play nice.

    When I tried to run my current application with your API libraries, I got a few weird errors. I assume it had to do with my use of HMVC, and I was able to track down and get rid of a couple. There’s one error that I can’t track down — the results I’ve found for this error aren’t too helpful:

    A PHP Error was encountered
    Severity: Notice
    Message: Undefined property: CI::$format
    Filename: MX/Loader.php
    Line Number: 165

    This is thrown regarding the format library you included (which I copied into my application/libraries directory) in REST_Controller line 46:

    $this->load->library(‘format’);

    I’m still fairly new to CI and definitely new to HMVC, and I was wondering if you had any suggestions as to what would be causing this. Did I not copy your files correctly or into the right locations? Is this an error with my HMVC install rather than your REST code?

    Thanks for building this and working so much on CI, I appreciate it.

    -Nick

    • Edmund

      I had this problem too. I didn’t have much time to debug everything, but I found out that loading Format library beforehand (adding it to autoload) and extending MX_Controller instead of CI_Controller in REST_Controller makes the error go away.

      However, it still won’t work (at least for me it didn’t). The problem was that REST_Controller looks for a non-existing method. For example, I have a class called User in controllers/rest/user.php.

      class User extends REST_Controller
      {
      function user_get()
      {
      $test = array(‘foo’ => ‘bar’);
      $this->response($test, 200);
      }
      }

      But when I try to access it via URL: localhost/my_test_app/rest/user/id/5, it tells me:

      {“status”:false,”error”:”Unknown method.”}

      It turns out that REST_Controller is looking for id_get() method, which obviously does not exist. So you’ll probably have to make some adjustments in the _remap() method to make it call user_get(). Or in general call ClassName_method() where method is get, put, post or delete.

      Hope it helps for someone.

      • http://edmundask.lt Edmund

        Sorry, wrote some nonsense here. Don’t pay attention to this non-existent method thing I wrote earlier. :)

        Although I noticed that if you have class name the same as the method (for example, class User and a method user_get()), it remaps to index_get(). I’m pretty sure HMVC is responsible for this.

      • http://philsturgeon.co.uk Phil Sturgeon
        Author

        It’s not a bug and has nothing to do with HMVC, the method is named in the URL just like anything else.

        If you dont pick a method, CodeIgniter will assume you mean the index method – which is standard behaviour. So if you don’t pick anything and make a get to /user then it will look for a class named User then go for index_get().

        And the only reason the error with the format library will happen is if you haven’t copied the format library. I didn’t mention it at the time of writing this article because it didn’t exist.

  • http://www.netmusician.org Joe Auty

    Phil,

    Doesn’t the “Format” class also need to be copied to the user’s library folder in addition to REST_Controller?

    Do you have any plans to make this library available as a Spark? This would be very handy, and ought to work now that the Spark core has been updated to read Spark config files, where the rest.php config file could go, right?

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      Yes it does need to be copied, but it was not mentioned while writing the article because I wrote that library two years later :)

      I will probably merge it with CodeIgniter in time for 3.0.

  • http://crypsis venu

    Hi,
    I am getting response from some methods …and i am not getting response from some other methods. I am using the basic $user = json_decode(
    file_get_contents(‘http://example.com/index.php/api/user/id/1/format/json’)
    );

    echo $user->name;

    to get the response… i am sending some string as a response after the user validation for login… Please help!

  • http://ejohn.me John

    I am using the rest client as follows and it does not seem to pass anything to the rest controller

    $params =array();
    $param['id']= $this->people_info['id'];
    $param['first_name']= $this->input->post(‘first_name’);
    $param['given_name']= $this->input->post(‘given_name’);
    $message=$this->rest->post(‘save_user’,$params);
    The following is code on the rest controller

    $message = array(‘id’ => $this->input->post(‘id’),
    ‘first_name’ => $this->input->post(‘first_name’),
    ‘last_name’ => $this->input->post(‘last_name’),
    ‘message’ => ‘Saved!’);
    $this->response($message, 200); // 200 being the HTTP response code

    The result
    bject(stdClass)#23 (4) { ["id"]=> bool(false) ["first_name"]=> bool(false) ["last_name"]=> bool(false) ["message"]=> string(6) “Saved!” }

    Am not sure what the rest client is not passing post data to the rest controller. I might be the damn guy doing something wrong

  • Tim

    Hi,

    It appears the the Apache HttpClient is unable to parse the WWW-Authenticate header (digest auth) because there are no comma separators between the key=”value” pairs.

    per RFC 2617 sec 1.2:
    “or if more than one WWW-Authenticate header field is provided, since the contents of a challenge may itself contain a comma-separated list of authentication parameters.”

    @see http://www.ietf.org/rfc/rfc2617.txt

    I’ve tested using command-line curl both with and without the commas, and it doesn’t seem to care (in fact, it’s Authenticate response header DOES include the commas).

    Diff:

    779 779 elseif ($this->config->item(‘rest_auth’) == ‘digest’)
    780 780 {
    781 header(‘WWW-Authenticate: Digest realm=”‘ . $this->config->item(‘rest_realm’) . ‘” qop=”auth” nonce=”‘ . $nonce . ‘” opaque=”‘ . md5($this->config->item(‘rest_realm’)) . ‘”‘);
    781 header(‘WWW-Authenticate: Digest realm=”‘ . $this->config->item(‘rest_realm’) . ‘”, qop=”auth”, nonce=”‘ . $nonce . ‘”, opaque=”‘ . md5($this->config->item(‘rest_realm’)) . ‘”‘);
    782 782 }

  • http://www.netmusician.org Joe Auty

    Isn’t the explanation for PUT and POST flip flopped, at least according to this? http://mvccontrib.codeplex.com/wikipage?title=SimplyRestfulRouting&referringTitle=Documentation&ProjectName=mvccontrib

  • Andrew Christensen

    I’m having a ton of trouble trying to use this library. I copied files over as described. I created a new controller and tried to implement it as described in the example_api file. But I just get a page can’t be found error. It simply doesn’t like extending the REST_Controller. If change that to CI_Controller and then make just the index function echo a word and not actually try to use anything in the REST_Controller.php file it displays the echoed text.

    I’m using the latest build of CI 2.0.3.

    What am I missing?

    Thanks Phil!

  • http://www.mboot.org Maarten

    The latest download contains a
    $this->load->library(‘security’);
    in REST_Controller.php
    this seems to case errors with the version of
    https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc/wiki/Home
    commenting out the line solves the problem, loohing at the user_guide for CI there is no reference to a library.

    Maarten

    P.S. my first steps with CI2 and HMVC and REST, worked with CI1.7 before

  • Alex

    There is a typo in several of the examples.

    require(APPPATH’.libraries/REST_Controller.php’);

    should be

    require(APPPATH.’libraries/REST_Controller.php’);

    The period is in the wrong place.

  • Volt

    Hi Phil,

    How can i do a post with headers and parameters? something like the -H on curl.

  • Raju

    Nice Tutorial,, While generating XML Format, How we can add attributes(Tags) to XML Elements and how we can put our own Element names for integers

  • http://christopherickes.com Chris

    Great tutorial Phil. I am struggling to properly pass non-simple json objects as a uri key/value pair. Any thoughts on how to properly format this?

    var requestObj = {
    city: ‘London’,
    taxes: [
    { name: 'Income Tax', value: '10%', 'applies_to': 'paycheck'},
    { name: 'Sales Tax', value: '7.5%', 'applies_to': 'sales'}
    ]
    };

    index.php/example_api/calculate/city/london/now_what?

    Thanks!

  • blessy

    Hi all

    How to use keys in CI REST what is the use ? How can I implement .I have successfully created other parts except Keys.please help me

  • http://geoglis.de Swen

    Please, would you add the following code-snippet to “Format.php” for coming releases:

    // Encode as JSONP
    public function to_jsonp()
    {
    return $this->input->get_post(‘callback’) . ‘(‘ . json_encode($this->_data) . ‘)’;
    }

    So everybody can use it in this way:

    http://…/api/example/users/id/1/format/jsonp/callback/test123456

  • http://moefelt.dk Moefelt

    This is soo sweet – endless opportunities!

    A small note on curl digest auth – you need to add the curlopt:
    curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);

    When using the keys:
    curl_setopt($curl_handle, CURLOPT_HTTPHEADER, array(
    ‘X-API-KEY:xxxxxx’,
    ‘Accept: json’ // use Accept to call the format instead of in the url
    ));

    But works like a charm!

    /Anders

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      Correct! If you want to login via digest then you need to mention that to curl. If you want a certain content-type back then you need to specify that with the Accept header and if you want to send content you set the Content-Type header.

      That is how HTTP works, so it’s how this controller works. :)

    • Wasif Karim

      I am able to secure the rest api using basic authentication. I want to make it more secure and want to use digest instead. However, I am unable to make digest work for me. Here is what works and what doesn’t

      1. When $config['rest_auth'] = ‘basic’; is set in rest.php, I am able to test the results and it works via the following

      ‘test@test.com’,
      ‘password’ => ‘admin123′,
      ‘device_id’ => ’9876′
      );

      $dataStr = json_encode($data);

      $url = “www.someurl.com”;
      $username = ‘admin’;
      $password = ’1234′;

      $ch = curl_init($url);
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, “POST”);
      curl_setopt($ch, CURLOPT_USERPWD, $username . ‘:’ . $password);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $dataStr);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’, ‘Content-Length: ‘.strlen($dataStr)));

      $result = curl_exec($ch);

      print_r($result);

      ?>

      2. However, when I change to $config['rest_auth'] = ‘digest’; in rest.php and use the following, I get User not authorized.

      ‘test@test.com’,
      ‘password’ => ‘admin123′,
      ‘device_id’ => ’9876′
      );

      $dataStr = json_encode($data);

      $url = “www.someurl.com”;
      $username = ‘admin’;
      $password = ’1234′;

      $ch = curl_init($url);
      curl_setopt($ch, CURLOPT_CUSTOMREQUEST, “POST”);
      curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
      curl_setopt($ch, CURLOPT_USERPWD, $username . ‘:’ . $password);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $dataStr);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Content-Type: application/json’, ‘Content-Length: ‘.strlen($dataStr)));

      $result = curl_exec($ch);

      print_r($result);

      ?>

      The only thing changed above was the inclusion of the following line:
      curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);

      and specifying digest in rest.php.

      Can you shed some light to this. Is digest secure even on http protocol?

  • apriza

    i have error when execute this method using post:
    php:
    public function gridData()
    {
    $this->curl->post(array(
    ‘page’ => $this->input->post(‘page’, true),
    ‘start’ => $this->input->post(‘start’, true),
    ‘limit’ => $this->input->post(‘limit’, true)
    ));
    $this->curl->create(config_item(‘rest_server_url’).’configuration/room_amenities/gridData/format/json’);
    die($this->curl->execute());
    }

    Error:
    ‘Unable to parse the JSON returned by the server: You’re trying to decode an invalid JSON String:

    A PHP Error was encountered

    Severity: Warning
    Message: curl_setopt_array(): supplied argument is not a valid cURL handle resource
    Filename: libraries/Curl.php
    Line Number: 232

    {“totalCount”:”3″,”data”:[{"id_amenity":"1","amenity_name":"AC","amenity_type_id":"1","amenity_sort_key":"a","brand_id":"1"},{"id_amenity":"2","amenity_name":"Bed Room","amenity_type_id":"1","amenity_sort_key":"b","brand_id":"1"},{"id_amenity":"3","amenity_name":"Bed Cover","amenity_type_id":"1","amenity_sort_key":"c","brand_id":"1"}]}’ when calling method: [nsIDOMEventListener::handleEvent]

    this is using cUrl.
    what should be changed from the curl libraries?

  • rp

    Very nice….thanks

  • http://granze.net Granze

    If I try to print_r the twitter example, i get an 404 error. What’s wrong?

    $this->load->spark(‘curl/1.2.0′);
    $this->load->spark(‘restclient/2.0.0′);

    $this->load->library(‘rest’, array(
    ‘server’ => ‘http://twitter.com/’,
    ‘http_user’ => ‘granze’,
    ‘http_pass’ => ‘MYPASS’,
    ‘http_auth’ => ‘basic’
    ));

    $data['tweets'] = $this->rest->get(‘statuses/user_timeline/granze.xml’);

    print_r($data['tweets']);

  • Jonathan

    Hi,

    I cannot use special characters like åäö in Post requests. Why?

    • http://www.arafah.me Marwan Arafah

      In the /application/config.php file find this variable:

      $config['permitted_uri_chars'] = ‘a-z 0-9~%.:_\-’;

      here u can choose the characters u want in the request, if u want all just set to empy

      $config['permitted_uri_chars'] = ”;

  • ramon

    thank you for making life simpler
    kisses all over

  • Wu Chia Chong

    Isn’t it possible to post an image file with this REST API?

  • sami

    Hi,
    while get and post example seems to work. I can’t get to run PUT and Delete. This gives me a 403 forbidden access error. Thanks for any help

  • Kuraiko

    Hello,

    maybe I’m stupid or blind but… I doesn’t understand how the thing with api-key levels works :/
    How can I set the level for a method?

    Thanks!

    Regards Kuraiko

  • Simone

    Ciao,
    I would like to thank you very much for your work, because it allowed me to create a web service that connects a Data Base and two client applications, for iPhone and Mac, without difficulty. This project is my graduation thesis and I gratuated 5 days ago.

    Thank you again :)

  • Yaroslav

    Hey,

    great work, its realy simply and usefull, thanks a lot!

    one note. _detect_method() function does not detect “PUT” and “DELETE” methods if you using cross-site requests.

    http://www.w3.org/TR/2008/WD-access-control-20080912/

    So, I just add one simply string to fix this, you can update your cool stuff, if want ;) take care!

    protected function _detect_method()
    {
    $method = strtolower($this->input->server(‘REQUEST_METHOD’));
    if (!in_array($method, array(‘get’, ‘delete’, ‘post’, ‘put’)))
    {
    $method = strtolower($this->input->server(‘HTTP_ACCESS_CONTROL_REQUEST_METHOD’));
    }
    $request_headers = apache_request_headers();
    ….

  • chrisc

    I use the REST API to perform as GET. The return data is cast as an Array but is a SImpleXMLElement. How do I cas it back to a SimpleXMLElement so I can use the SimpleXMLElelment methods on it.

  • Daniel

    Hi,

    Thanks for these great libraries Phil!

    I have a REST File Upload question.

    I’m a PHP novice, but I’ve been trying to implement a file uploader from the restclient to the restserver. All the tutorials about PHP file upload I’ve found have involved Forms.

    I’ve tried to implement file_get_contents and file_put_contents (with various combinations of urlencode and base64_endcode), and have played around with cURL, all without any joy. I get these behaviours:

    - for small text files – WORKS FINE!
    - for images (such as png) – the best I’ve got is all the data (I think), but a garbled image
    - for large text files (>10MB) – nothing works (and I’ve updated my php.ini limits accordingly)

    I think it might have something to do with Content-types, but I’m really not sure. It’d be great to utilise the built-in Codeigniter Upload Library somehow (I’ve had the Form File Upload demo working fine), but my limited understand of PHP is hampering me.

    Please can someone help?!

    Thanks.

    • Daniel

      Actually I just managed to get it working (after 2 days!).

      Restclient code:

      public function postFile()
      {
      $filename = “/some/path/to/file.xyz” ;
      $data = base64_encode(file_get_contents($filename)) ;
      $this-&gtrest-&gtpost(‘upload’,array(‘afile’ =&gt $data),’text/plain’);
      }

      Restserver code:

      public function upload_post()
      {
      file_put_contents(‘./uploads/works.xyz’, base64_decode($this-&gtpost(‘afile’))) ;
      }

      This is just a hack, and very inelegant (as per my PHP skills), so if anyone has a better, more complete solution (that uses the built-in upload library), then I’d be very keen to see it.

      Thanks.

      • Daniel

        Hi,

        Whilst the RESTserver works for small file uploads, using this very basic code:

        Restclient code:

        public function postFile()
        {
        $filename = “/some/path/to/file.xyz” ;
        $data = base64_encode(file_get_contents($filename)) ;
        $this->rest->post(‘upload’,array(‘afile’ => $data),’text/plain’);
        }

        Restserver code:

        public function upload_post()
        {
        file_put_contents(‘./uploads/works.xyz’, base64_decode($this->post(‘afile’))) ;
        }

        ….it doesn’t work for large files! I’ve tried ‘chunk_split’, but without success. I think this has something to do with cURL (and specifically the READFUNCTION option), but after spending hours trawling through forums I still haven’t been able to figure it out.

        Can anyone please help with large file upload using this RESTserver?

        Thanks!

  • Stephen

    I implemented the RESTful service, but not able to establish routes in the routes.php file. I am trying to change the routing to support the standard RESTful defined by Roy Fielding that would support {baseURI}/dogs/{id} uses index method, rather than {baseURI}/dogs/dog/id/{id} uses dog method. Also support {baseURI}/dogs/{id}/owners returns all owners for the dog specified by the {id}, or {baseURI}/dogs/{id}/owners/{id} returns owner {id} for the dog {id}.

    Any suggestions on how best to modify this library to support this approach?

  • http://www.fai-brasil.com Ricardo

    Very good job Phil. I was able to put a rest server up in 10 minutes, and and not a PHP developer!! Really helped me a lot, many thanks indeed.

    One footnote though, I had some problems with compressed responses, it seems that Codeigniter compressed responses are enabled by default. I had to add this line to my methods for things working smoothly:

    error_reporting(E_ALL ^ E_NOTICE);

    My browser was complaining about improper response. I’ve tested on Firefox, Chrome and Iexplorer and all of them were giving me that answer.

  • Adrien

    Hi all.

    First of all, thanks Phil for all your CI stuff :)

    I use the RESTFul server version and JSON export format.

    In local, I have this result :
    {“error”:”Country does not exist”}

    That’s an error message, it’s normal for the test.

    But in my remote server, for the same request I have this :
    {“error”:”Country does

    The message is cut.
    Do you know why ?

  • Ciro Mine

    Hi Phill in the first place I thank you for all your work and I apologize, but my English is very bad.

    I have the following questions, there are times when I send a GET message with some character like this: “!, ,(,)” For example, gives me the following error message:

    “An Error Was Encountered
    The URI you submitted has disallowed characters. ”

    is there any way to handle these characters? since this can be very annoying for the development I’m doing, greetings and thanks.

  • http://bitcoinsecurity.com Bitcoin Security

    Hi, thanks for the tutorial, very interesting.

    However the security does concern me a bit. Sending user:pass over a plain text connection in the example?

    Anyway it was helpful thanks, and I will try to modify the authentication.

    • http://philsturgeon.co.uk Phil Sturgeon
      Author

      That is why HTTP Basic auth is horrendously insecure, as it means anyone monitoring network traffic can pick up a password. That said it’s no different to OAuth2, where somebody can just snoop an access token.

      Use HTTPS and you’ll be fine, otherwise you’ll get hacked in no time.

  • http://www.eyeway.gr George

    Great tutorial Phil! Thanks.
    I used your tutorial and your code to build a RESTful application with great ease.
    Only I have this problem: When I test my application on my local Apache server everything works 100%.
    When I upload the same application to my test server on the internet, most of the times my REST requests to the server fail with a ” Connection reset by peer” Error message.
    The curious thing is that this is not happening consistently. 80% of the time the requests fail but there are times where the same request passes to the server and produces results.
    I suspect timout problem with cURL and for that reason I tried increasing connect timeout ( CURLOPT_CONNECTTIMEOUT) but with no result.

    In my application, both in client and server, I have moved all logic to my models and for the client I am loading REST library in my model.
    I use the controllers only as traffic agents.

    Do you think that this is wrong or this it the reason of my cURL issues?

    Thanks again.

    George

  • iDVB

    Hey Phil, great work.

    I have the server working perfectly with GET however, when POST ‘n $this->post() always equals FALSE.
    I’ve spent the last 5 hours reading about the base_url, CSRF etc trying to troubleshoot the issue with no luck. I’ve tried using JQuery to POST as well as using the OSX app “HTTP Client”.

    Do you have any idea why I can’t seem to get POST vars?

    • fons

      Same Problem here :( Cannot retrieve POST vars, always Return false