Web Services Tutorial
Web Services Tutorial
http://bit.ly/oaxVdy
(while youre waiting, download the les!)
About Me
Lorna Jane Mitchell PHP Consultant/Developer Author API Specialist Project Lead on http://joind.in Twitter: @lornajane Website: http://lornajane.net
The Plan
Talk about web services in general Build a web service Consume a web service Talk about how to make excellent web services
The Plan
Talk about web services in general Build a web service Consume a web service Talk about how to make excellent web services (well hide some actual textbook theory in there somewhere) Ask questions!
Theory and Introduction
What Is A Web Service?
Means of exposing functionality or data A lot like a web page, but for machines Integration between applications Separation within an application
Integration Between Applications
Common to use an API internally as well as exposing it
Website
3rd Party / Apps
API
Data Store
7
Separation Within an Application
Routing
Auth
Images
Products
Orders
Separation Within an Application
Routing
Auth
Images
Products
Orders
Web Services and You
This is not rocket science You already know most of what you need!
10
Lets Begin
Building Blocks
You can make an API from any tools you like Existing MVC setup Simple PHP (as in these examples) Framework modules Component library
12
My First Web Service
Make a virtual host e.g. http://api.local Dont forget to restart apache Add an entry to your hosts le
<VirtualHost *:80> ServerName api.local ServerAdmin admin@localhost DocumentRoot /var/www/myapi/public <Directory /var/www/myapi/public> AllowOverride All Order deny,allow Allow from All </Directory> </VirtualHost>
api.vhost
13
My First Web Service
Create the index.php le e.g. /var/www/myapi/public/index.php
$data = array( 'format' => 'json', 'status' => 'live' ); echo json_encode($data);
public/index.php
14
Consume Your Service
Use cURL from the command line curl http://api.local For more information about curl: http://curl.haxx.se/ - curl homepage http://bit.ly/esqBmz - my cheat sheet
15
JSON JavaScript Object Notation
Originally for JavaScript Native read/write in most languages Simple, lightweight format - useful for mobile In PHP we have json_encode() and json_decode() These work with arrays and objects Our service returns:
{'format':'json','status':'live'}
16
Heartbeat Method
A method which does nothing No authentication Requires correct request format Gives basic feedback Shows that service is alive
17
Delivering A Web Service
Service Documentation Examples A help point
18
HTTP and Data Formats
HTTP
HTTP is Hypertext Transfer Protocol. It is designed to exchange information about a request/response. Status Codes (e.g. 200, 404) Headers (e.g. Content-Type, Authorization) Verbs (e.g GET, POST)
20
Status Codes
Win or fail? Some common codes: 200 204 302 301 302 400 401 403 404 500 OK No Content Found Moved Permanently Found Bad Request Not Authorised Forbidden Not Found Internal Server Error
21
For more, see: http://bitly.com/h03Dxe
Working with Status Codes in PHP
We can observe status codes with curl, passing the -I switch
curl -I http://api.local
Lets amend our web service, to return a 302 header
header("302 Found", true, 302); $data = array( 'format' => 'json', 'status' => 'live' ); echo json_encode($data);
22
HTTP Verbs
More than GET and POST PUT and DELETE to update and delete in a RESTful service HEAD, OPTIONS and others also specied GET In REST, we use: POST PUT DELETE Read Create Update Delete
23
HTTP Headers
Headers are the metadata about the content we send/receive Useful headers: Accept and Content-Type: used for content format negotiation User-Agent: to identify what made the request Set-Cookie and Cookie: working with cookie data Authorization: controlling access
24
Accept Header
What type of content can the consumer understand? -v with curl to see request and response headers -H to add headers
curl -v -H "Accept: text/html" http://api.local
Gives the output:
* * * > > > > >
About to connect() to api.local port 80 (#0) Trying 127.0.0.1... connected Connected to api.local (127.0.0.1) port 80 (#0) GET / HTTP/1.1 User-Agent: curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0 Host: api.local Accept: text/html
25
Using the Accept Header
We can work out what format the user wanted to see from the Accept header.
$data = array( 'status' => 'live', 'now' => time() ); if(false !== strpos($_SERVER['HTTP_ACCEPT'], 'text/html')) { echo "<pre>"; print_r($data); echo "</pre>"; } else { // return json echo json_encode($data); }
public/headers.php
26
How to REALLY Handle Accept Headers
Example accept header (from my browser)
text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
See a much nicer example of this in headers-accept.php Taken almost entirely from the source of arbitracker http://arbitracker.org src/classes/request/parser.php Try this from curl, setting your own headers, and from your browser
27
Content-Type Header
The Content-Type header: literally labels the contents of the response. We can include these in our examples:
$data = array( 'status' => 'live', 'now' => time() ); if(false !== strpos($_SERVER['HTTP_ACCEPT'], 'text/html')) { header('Content-Type: text/html'); echo "<pre>"; print_r($data); echo "</pre>"; } else { // return json header('Content-Type: application/json'); echo json_encode($data); }
public/headers.php
28
Handling XML Formats
We can work with XML in PHP almost as easily Content type is text/xml or application/xml Two XML libraries in PHP SimpleXML bit.ly/g1xpaP DOM bit.ly/e0XMzd Give consumers a choice of formats: HTML, XML, JSON ...
29
Adding XML to Our Service
$data = array( 'status' => 'live', 'now' => time() ); $simplexml = simplexml_load_string('<?xml version="1.0" ?><data />'); foreach($data as $key => $value) { $simplexml->addChild($key, $value); } header('Content-Type: text/xml'); echo $simplexml->asXML();
public/headers.php
The result is this:
<?xml version="1.0"?> <data><status>live</status><now>1302981884</now></data>
30
Versioning
Always include a version parameter or media type
http://example.com/api/v4/status http://example.com/api/status
Content-Type:
application/vnd.myapi.2+json
31
Data Formats
Handle multiple formats, by header or parameter JSON XML ? Common to detect header, allow parameter override
http://example.com/api/v4/status
Accept:
application/json
http://example.com/api/v4/status?format=json
Accept:
text/html
32
Statelessness
Request alone contains all information needed No data persistence between requests Resource does not need to be in known state Same operation performs same outcome
33
Consuming Services from PHP
Consuming Your First Web Service
Three ways to consume web services: Via streams (e.g. file_get_contents()) Using the curl extension http://lrnja.net/I5JD9R Using Pecl_HTTP http://lrnja.net/I23yZj
35
Using File_Get_Contents
This is the simplest, especially for a GET request
$response = file_get_contents('http://api.local/'); var_dump($response);
You can set more information in the stream context bit.ly/gxBAgV
36
Using Curl
This extension is commonly available
$ch = curl_init('http://api.local/'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); var_dump($response);
Look out for the CURLOPT_RETURNTRANSFER; without it, this will echo the output
37
Using Pecl_HTTP
This is the most powerful and exible, and can easily be installed from
http://pecl.php.net
$request = new HTTPRequest('http://api.local/', HTTP_METH_GET); $request->send(); $response = $request->getResponseBody(); var_dump($response);
Strongly recommend pecl_http if you are able to install pecl modules on your platform
38
Service Types
Web Service Types
There are a few types of web service RESTful RPC (Remote Procedure Call) XML-RPC JSON-RPC Soap
40
RPC Services
These services typically have: A single endpoint Method names Method parameters A return value Often all POST requests A familiar model for us as developers
41
Soap
Not an acronym (used to stand for Simple Object Access Protocol) Special case of XML-RPC VERY easy to do in PHP Can be used with a WSDL Web Service Description Language
42
Soap Example: Library Class
public function eightBall() { $options = array( "Without a doubt", "As I see it, yes", "Most likely", "Reply hazy, try again", "Better not tell you now", "Concentrate and ask again", "Don't count on it", "Very doubtful"); return $options[array_rand($options)]; }
/public/soap/Library.php
43
Soap Server
(dont blink or you will miss it!)
include('Library.php'); $options = array('uri' => 'http://api.local/soap'); $server = new SoapServer(NULL, $options); $server->setClass('Library'); $server->handle();
/public/soap/index.php
44
Consuming a Soap Service
To call PHP directly, we would do:
include('Library.php'); $lib = new Library(); $response = $lib->eightBall(); echo $response;
Over Soap:
$options = array('uri' => 'http://myapi.local', 'location' => 'http://myapi.local/soap/'); try{ $client = new SoapClient(NULL, $options); $response = $client->eightBall(); echo $response; } catch (SoapFault $e) { echo "ERROR: " . $e->getMessage(); }
45
Pros and Cons of Soap
Many languages have libraries for it .NET and Java programmers in particular like it
46
Pros and Cons of Soap
Many languages have libraries for it .NET and Java programmers in particular like it Weird things happen between languages regarding data types When it works, its marvellous When it doesnt work, its horrid to debug (so Ill show you how)
46
Pros and Cons of Soap
Many languages have libraries for it .NET and Java programmers in particular like it Weird things happen between languages regarding data types When it works, its marvellous When it doesnt work, its horrid to debug (so Ill show you how) WSDL is complicated!
46
Working with WSDLs
WSDL stands for Web Service Description Language WSDLs were not designed for humans to read They are written in XML, and are very verbose If you do need to read one, start at the END and read upwards in sections Soap uses very strict data typing, which is an unknown concept in PHP Read about WSDLs: bit.ly/eNZOmp
47
Debugging Soap
The Soap extension has some debug methods: Set the options to include trace=1 in the SoapClient getLastRequest(), getLastRequestHeaders(), getLastResponse(), getLastResponseHeaders() are then available For all web service types, you can use: I like Wireshark (http://www.wireshark.org) Others use Charles (http://www.charlesproxy.com) If you like reading XML then use curl SoapUI (http://soapui.org)
48
Extending Our Service
Consistency
Important to retain Naming conventions Parameter validation rules Parameter order Just as you would in library code
50
The Action Parameter
As a simple place to start, lets add an action parameter.
// route the request (filter input!) $action = $_GET['action']; $library = new Actions(); $data = $library->$action();
/rpc/index.php
Heres the code for the Actions class
class Actions { public function getSiteStatus() { return array('status' => 'healthy', 'time' => date('d-M-Y')); } }
/rpc/actions.php
So try http://api.local/rpc/index.php?action=getSiteStatus
51
Small APIs
Beware adding new functionality Simple is maintainable Easy to use and understand
52
Adding an Action
To add a new action, create a new method for the Actions class, returning an array to pass to the output handlers
public function addTwoNumbers($params) { return array('result' => ($params['a'] + $params['b'])); }
/rpc/actions.php
But how do we pass the parameters in? Something like this
// route the request (filter input!) $action = $_GET['action']; $library = new Actions(); // OBVIOUSLY you would filter input at this point $data = $library->$action($_GET);
/rpc/index.php
53
Access Control
A few common ways to identify users Username and password with every request Login action and give a token to use OAuth For internal applications, rewalls
54
RPC Service Example: Flickr
Take a look at http://www.flickr.com/services/api/ Supports multiple formats (for request and response) Is well-documented and consistent Has libraries helping users to consume it Offers both RPC and RESTful (kind of!) interfaces Follows true XML-RPC (see
http://en.wikipedia.org/wiki/Xml-rpc)
Vast numbers of applications using this API to provide ickr functionality elsewhere
55
RPC vs REST
Weve seen an RPC service, but what about REST? The two are quite different RPC services describe protocols, e.g. XML-RPC RPC is a familiar: functions and arguments REST is a set of principles Takes advantage of HTTP features Typically has "pretty URLs", e.g. Twitter is mostly RESTful
56
RESTful Web Service
REST
REpresentational State Transfer URLs are unique resource identiers HTTP verbs indicate which operation should happen We have full CRUD operations on a series of resources Heres one I prepared earlier: /public/rest/
58
REST as a Religion
Beware publishing a service labelled "RESTful" Someone will always tell you it isnt They are probably right The strict rules around REST sometimes t badly around business requirements Flamewars will ensue Instead: "An HTTP Web Service"
59
Making Our Service Restful
Were aiming for URLs that look something like this: http://api.local/rest/user http://api.local/rest/user/3 http://api.local/rest/user/3/friends A specic item is a resource; where you request a list, e.g. /user, this is called a collection
60
All Requests to index.php
We want to push all requests through index.php, there is an .htaccess le that does this
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [L] </IfModule>
/public/rest/.htaccess
61
Routing Within Our App
So our routing depends on the URL and on the verb used, so our controllers have a method per verb: GET /user Becomes UserController::GETAction()
// route the request (filter input!) $verb = $_SERVER['REQUEST_METHOD']; $action_name = strtoupper($verb) . 'Action'; $url_params = explode('/',$_SERVER['PATH_INFO']); $controller_name = ucfirst($url_params[1]) . 'Controller'; $controller = new $controller_name(); $data = $controller->$action_name($url_params); // output appropriately $view->render($data);
/public/rest/index.php
62
A Simple Controller
Now in UserController we add a GETAction
class UsersController { public function GETAction($parameters) { $users = array(); // imagine retreving data from models if(isset($parameters[2])) { return $users[(int)$parameters[2]]; } else { return $users; } } }
/public/rest/controllers.php
63
Extending our Service
Next we will add something to get the users friends
http://api.local/users/1/friends
class UsersController { public function GETAction($parameters) { $users = array(); $friends = array(); // imagine retreving data from models
if(isset($parameters[2])) { if(isset($parameters[3]) && $parameters[3] == 'friends') { return $friends[(int)$parameters[2]]; } else { return $users[(int)$parameters[2]]; } } else { return $users; } } }
/public/rest/controllers.php
64
Hypermedia
The items returned are resources with their URLs This is hypermedia - providing links to related items/collections Allows a service to be self-documenting Allows a service to change its URLs - because theyre provided
65
Creating New Records
RESTful services use POST for creating data, so we have POSTAction curl -X POST http://api.local/users -d name="Fred Weasley"
public function POSTAction() { // sanitise and validate data please! $data = $_POST; // create a user from params $user['name'] = $data['name']; // save the user, return the new id // redirect user header('201 Created', true, 201); header('Location: http://api.local/rest/users/5'); return $user; }
/public/rest/controllers.php
66
Updating Records
To create: we used POST To update: we use PUT The code looks basically the same, apart from:
// instead of this: $data = $_POST; // use this: parse_str(file_get_contents('php://input'), $data);
67
Appropriate Status Codes for REST
GET 200 or maybe 302 if we found it somewhere else 404 if we cant nd it POST 201 and a location header to the new resource or 400 if we cant create an item PUT 204 for OK (but no content) 400 if the supplied data was invalid DELETE 200 at all times, for idempotency
68
Responding to Failure
Use expected response format Collate all errors and return Help users help themselves Be consistent Be accurate
69
Delivering Services
Technical Aspects
All PHP Best Practices apply equally to APIs Source control Unit/Integration testing Automated deployment Monitoring Reliability and consistency are key
71
User Aspects
Who will use your API? Offer API Docs Write a quick start for the impatient Show examples, as many as possible Tutorials, blog posts, links to forum answers, anything Respond to questions
72
Questions?
Resources
All links are in the bit.ly bundle bit.ly/emRpYT
74
Thanks
https://joind.in/6217
@lornajane
http://lornajane.net
75