by Enrico Zimuel / @ezimuel
Senior Software Engineer
Zend Technologies, a Rogue Wave Company
Oct 19, ZendCon 2016, Las Vegas
|
API stands for Application Programming Interface and as a term, specifies how software should interact.
Web APIs are delivered over HTTP.
GET /api/test
HTTP/1.1 200 OK
Connection: close
Content-Length: 14
Content-Type: application/json
{
"msg": "test"
}
A function that gets a request and generates a response
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
function (Request $request, Response $response) {
// manipulate $request to generate a $response
return $response;
}
Use an additional callable during the invoke ($next)
class Middleware
{
public function __invoke(
\Psr\Http\Message\ServerRequestInterface $request,
\Psr\Http\Message\ResponseInterface $response,
callable $next = null
) {
// do something before
if ($next) {
$next($request, $response);
}
// do something after
return $response;
}
}
function ($request, $response, $next)
use Zend\Expressive\AppFactory;
use Zend\Diactoros\Response\JsonResponse;
chdir(dirname(__DIR__));
require 'vendor/autoload.php';
$app = AppFactory::create();
$app->get('/api/ping', function ($request, $response, $next) {
return new JsonResponse(['ack' => time()]);
});
$app->pipeRoutingMiddleware();
$app->pipeDispatchMiddleware();
$app->run();
GET /api/ping
HTTP/1.1 200 OK
Connection: close
Content-Length: 18
Content-Type: application/json
{
"ack": 1476837112
}
GET /api/foo
HTTP/1.1 404 Not Found
Connection: close
Content-Length: 41
Content-type: text/html; charset=UTF-8
Cannot GET http://hostname/api/foo
POST /api/ping
HTTP/1.1 405 Method Not Allowed
Allow: GET
Connection: close
Content-Length: 18
Content-type: text/html; charset=UTF-8
Method Not Allowed
$app->get('/api/hello[/[{name}]]',
function ($request, $response, $next) {
$name = $request->getAttribute('name') ?? 'Mr. Robot';
return new JsonResponse(['user' => $name ]);
}
);
FastRoute syntax for route
We provide a Body Parsing Middleware helper:
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
$app->pipe(BodyParamsMiddleware::class);
You can use $request->getParsedBody() in request
$app->post('/api/user[/]',
function ($request, $response, $next) {
$data = $request->getParsedBody();
// if JSON $data will contain the array of keys => values
...
}
);
REpresentational State Transfer (REST) is an architecture designed around the HTTP specification.
REST leverages HTTP's strengths, and builds on:
Route definition:
$app->get('/api/user[/{id:\d+}]', Middleware\User::class);
$app->post('/api/user', Middleware\User::class);
$app->patch('/api/user/{id:\d+}', Middleware\User::class);
$app->delete('/api/user/{id:\d+}', Middleware\User::class);
trait RestDispatchTrait
{
public function __invoke($request, $response, $next = null)
{
$method = strtolower($request->getMethod());
if (method_exists($this, $method)) {
return $this->$method($request, $response, $next);
}
return $response->withStatus(501); // Method not implemented
}
}
class UserMiddleware
{
use RestDispatchTrait;
public function get($request, $response, $next = null) {}
public function post($request, $response, $next = null) {}
public function patch($request, $response, $next = null) {}
public function delete($request, $response, $next = null) {}
}
RESTFUL = REST + hypermedia
PHP libraries to support hypermedia:
GET /api/user/1
{
"_links": {
"self": {
"href": "http://domain/api/user/1"
},
"contacts": [
{ "href": "http://domain/api/user/21" },
{ "href": "http://domain/api/user/33" }
]
},
"id": "1",
"name": "Foo",
"email": "foo@host.com"
}
Note: they require SSL/TLS to provide security
Algorithm in PHP:
base64_encode("$username:$password")
Client request:
GET /foo HTTP/1.1
Accept: application/json
Authorization: Basic cmFscGg6cmFscGg=
Algorithm in PHP:
$ha1 = md5("$username:$realm:$password");
$ha2 = md5("$method:$digestURI");
$response = md5("$ha1:$nonce:$ha2");
Client request (user "Mufasa", password "Circle Of Life")
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
https://github.com/ezimuel/zend-expressive-api
Note: Still a work-in-progress!
Please rate this talk at:
https://joind.in/talk/dedbd