Share
Embed
Web Frontend, API Backend
Introduction to building a PHP application with separate front and back ends.
Lorna Mitchell
October 29, 2014
Transcript
48 slides
-
How Would You Do That? Hint: using the skills you
already have -
Slim Framework http://www.slimframework.com/ • modern microframework • offers routing out
of the box • lightweight • extensible • actively developed and well supported -
API Endpoints Our API will have two endpoints initially: •
a list of events /events • an individual event /events/42 Using Slim Framework, let's put those in place -
Events List Controller Slim registers a callback per route 24
$app->get('/events', function () use ($app) { 25 $db = $app->config('container')['db']; 26 $data = array(); 27 28 $model = new EventModel($db); 29 $data['events'] = $model->getSomeEvents(); 30 31 $app->render("foo.php", array("mydata" => $data)); 32 }); 33 -
Events List Model 1 public function getSomeEvents() { 2 //
get all future events 3 $sql = "select ID, event_name, event_loc, event_desc " 4 . "from events " 5 . "where event_start > :start " 6 . "order by event_start " 7 . "limit 10"; 8 9 $stmt = $this->db->prepare($sql); 10 $stmt->execute(array("start" => mktime(0,0,0))); 11 $results = $stmt->fetchAll(PDO::FETCH_ASSOC); 12 return $results; 13 } 14 -
Output Handler 1 <?php 2 3 class View extends \Slim\View
{ 4 public function render($template) { 5 $app = \Slim\Slim::getInstance(); 6 $app->response->headers->set('Content-Type', 7 'application/json'); 8 return json_encode($this->data['mydata']); 9 } 10 } -
Fetching the Events List GET http://localhost:8880/events { "events": [ {
"ID": "44", "event_desc": "Praesent rutrum orci eget ipsum ornare et consequat neque egestas. Praesent rutrum orci eget ipsum ornare et consequat neque egestas. Lo "event_loc": "Bridgeport", "event_name": "CentOS day" }, { "ID": "6", "event_desc": "Vivamus gravida, dolor ut porta bibendum, mauris ligula condimentum est, id facilisis ante massa a justo. Nulla faucibus mollis ipsum si "event_loc": "Nectar", "event_name": "Ruby day" }, { "ID": "48", "event_desc": "Etiam ligula elit, condimentum lacinia fermentum nec, elementum id urna. Praesent rutrum orci eget ipsum ornare et consequat neque egest "event_loc": "New Hope", "event_name": "IT meetup" }, ... -
Frontend Building Blocks This project is quick-started with: • Slim
Framework again http://www.slimframework.com • Guzzle http://guzzlephp.org • PureCSS http://purecss.io/ (including their sample layout) • Brightened up with a little something from https://en.gravatar.com/ -
Events List Controller Slim registers a callback per route 16
$app->get('/', function () use ($app) { 17 $client = new ApiClient(new GuzzleHttp\Client()); 18 $events = $client->getEventList(); 19 $app->render("index.php", array("events" => $events)); 20 }); 21 -
Api Client 4 class ApiClient { 5 protected $client; 6
7 public function __construct(GuzzleHttp\Client $client) { 8 $this->client = $client; 9 } 10 11 public function getEventList() { 12 // todo make this URL configurable 13 $response = $this->client->get( 14 "http://localhost:8880/events"); 15 return $response->json(); 16 } 17 -
Template 34 <?php foreach($events['events'] as $event): ?> 35 <section class="post">
36 <header class="post-header"> 37 <h2 class="post-title"><?=$event['event_name']?></h2> 38 <img class="post-avatar" alt="avatar" height="48" width="48" 39 src="http://www.gravatar.com/avatar/<?=md5($event['ID'])?>?d=identicon"> 40 <p class="post-meta"> 41 More details: <a class="post-author" 42 href="/showEvent/<?=$event['ID']?>">click here</a> 43 </p> 44 </header> 45 </section> 46 <?php endforeach; //events ?> 47 -
The Gravatar Code Again To get one of those pattern
things, the URL is: http://www.gravatar.com/avatar/[hash] ?d=identicon -
Additional Considerations • identifying consumers • logging users in •
picking a data format • documentation and testing -
Identifying Consumers How open should your API be? Identify consumers
to: • control access to resources • enable rate-limiting • track how the API is being used -
How To Identify Consumers 1. give them an API key
2. check the API key on all requests -
Logging Users In Please use a standard! Tokens are nicer
than credentials -
OAuth 2 OAuth2 solves trust and authentication issues between user,
server and client. -
How OAuth Works Consumer sends Authorization Grant to server. (Full
details at http://tools.ietf.org/html/rfc6749) Server supplies an access token in response -
OAuth2 Authorization Grants Authorization Code For untrusted clients; send user
to website to log in, return an auth code Implicit Give the client an access token instead of an API key Client Credentials Use your own credentials as an access token Resource Owner Password Credentials Client exchanges user creds for an access token and only stores that -
Authorizations Endpoint 48 $app->post('/authorizations', function () use ($app) { 49
$db = $app->config('container')['db']; 50 $data = array(); 51 52 // horribly assuming JSON. Real code checks first 53 $in = json_decode(file_get_contents("php://input"), true); 54 55 $model = new AuthModel($db); 56 $data['access_token'] = $model->getAccessTokenFromCreds( 57 $in['consumer'], $in['username'], $in['password']); 58 $app->render("foo.php", array("mydata" => $data)); 59 }); 60 -
Making the Access Token 11 public function getAccessTokenFromCreds( 12 $consumer,
$username, $password) { 13 14 $sql = "select ID, username, password from user " 15 . "where username = :username " 16 . "and password = :password "; 17 18 $stmt = $this->db->prepare($sql); 19 $stmt->execute(array("username" => $username, 20 "password" => md5($password))); 21 $user_info = $stmt->fetch(PDO::FETCH_ASSOC); 22 -
Making the Access Token 25 $token_sql = "insert into oauth_access_tokens
" 26 . "set consumer_key = :consumer, " 27 . "user_id = :user_id, " 28 . "access_token = :token "; 29 30 // get random number and hash it for token 31 $token = bin2hex(openssl_random_pseudo_bytes(16)); 32 $token_stmt = $this->db->prepare($token_sql); 33 $token_stmt->execute(array( 34 "user_id" => $user_info['ID'], 35 "consumer" = $consumer, "token" => $token)); 36 return $token; 37 } 38 -
Data Formats • JSON or XML? • use Content-Type to
specify which • be specific by using "Media Types" -
Media Types Take the Content-Type header to the next level!
Consider these: • application/json • application/vnd.github+json • application/vnd.github.v3+json https://en.wikipedia.org/wiki/Internet_media_type -
Hypermedia • add links to related resources • for bonus
points, use a standard like HAL { "_links": { "self": { "href": "/things/42" }, "messages": {"href": "/things/42/messages" }, "user": { "href": "/users/123" } } } http://stateless.co/hal_specification.html -
Documentation Lots of options: • old-fashioned, write words and copy/paste
examples • static plus something like http://hurl.it • interactive docs: try https://github.com/mashery/iodocs • http://apiary.io/ produces a mock API, then verifies your real one -
Frontend, Backend • independently scalable/deployable/maintainable • reusable backend • modular
systems • flexible when requirements change -
Questions? While this slide is up, there may be interest
in: • Feedback https://m.joind.in/talk/1af2a (from http://oreilly.com) • my blog: http://lornajane.net • joind.in itself http://m.joind.in/about