Criando APIs com o Respect Framework
Jean Lucas de Carvalho
Desenvolvedor Frontend


2007
- 16 anos
- Primeiro site
- www.letsgyn.com
Let's Gyn
HTML
>
PHP
- Cadastro de eventos
- Postagem de fotos
- Redimensionamento automático
- Inserção de logo automático
- ...
Ganhei TEMPO
APRENDI PHP
2009
- Sistemas de Informação na UFG
- 2 Estágios em desenvolvimento PHP
- PHP puro
-
Wordpress
2012
- Desenvolvedor Frontend no LabTIME/UFG
- Código Frankenstein
- Migração para Bootstrap
- Tempo para estudo
- Frontend Moderno (Grunt, Yeoman, Bower, etc.)
Projeto IKKI
- AngularJS
- APIs REST
Google Developer BUS BRAZIL
- AngularJS
- APIs REST
- Ionic Framework
PRESENTIO
- WebApp
- AngularJS
- APIs REST
- Facebook APIs
- MongoDB
Sugestão de presentes para amigos baseado no perfil do facebook
API
Application Programming Interface
UMA API, Vários Clientes
ResT
Por que REST?
- Escalabilidade
- Generalização
- Independência
- Latência
- Segurança
- Encapsulamento
Por que JSON?
- Ubiquidade
- Simplicidade
- Legibilidade
- Escalabilidade
- Flexibilidade
- Estilo de arquitetura
- Não é um padrão rigoroso
- Busca maximizar produtividade do desenvolvedor
- É um problema de projeto
- Mantenha o simples, simples
O que não Fazer
- /getBeer
- /getLocation
- /SaveBeer
- /getAllBeers
- /getLoggedUserInfo
- /getBeerTypes
- /saveBeerType
- /get
- /upsertBeer
- /beerState
URL Base simples e intuitiva
-
2 URLs base por recurso
- /beers
- /beers/12
- Não utilize verbos em suas URLs base
-
Use os métodos HTTP
- POST
- GET
- PUT
- DELETE
Escolhendo os substantivos das urls
-
Dê preferência a substantivos no plural
-
Foursquare
- /checkins
-
Dropbox
- /files
-
Foursquare
- Utilize substantivos concretos e não os abstratos
-
-
Abstratos
- /items
-
Concretos
- /blogs
- /news
-
Abstratos
Simplifique as associações
- GET /folders/523/files
- Retorna todos os arquivos pertencentes ao diretório informado
- POST /folders/523/files
- Cria um novo arquivo no diretório informado
Coloque a complexidade após o "?"
-
Atributos
- GET /beers?type=pielsen&location=BR
-
Paginação
- GET /beers?limit=10&offset=30
-
Busca Global
- GET /search?q=heineken
-
Busca por escopo
- GET /users/546/beers?q=heineken
Trate os erros
- Desenvolvedores aprendem a desenvolver através dos erros
- Desenvolvedores dependem de erros bem projetados para os ajudar em situações críticas
Erros
HTTP Status Code: 200
{
error: {
message: "Malformed access token <token>",
type: "OAuthException",
code: "190"
}
}Erros
- Twilio
HTTP Status Code: 401
{
status: "401",
message: "Authenticate",
code: 20003,
info: "http://www.twilio.com/docs/errors/20003"
}
Boas Práticas
- Use os códigos de estado do HTTP
- Retorne as mensagens de erro mais verbosas
-
Comece usando os seguintes códigos:
- 200 OK
- 400 Bad Request
- 500 Internal Server Error
Ações
-
Use verbos no lugar de substantivos
- GET /convert?from=EUR&to=RS&amount=200
- Deixe claro em sua documentação que são ações e não recursos
PHP PURO
- Métodos HTTP
- Rotas
- Organização de código
- Complexidade
- Segurança
- etc.
Slim Framework
<?php
//instancie o objeto
$app = new \Slim\Slim();
//defina a rota
get('/', function () {
echo "Hello, World!";
});
//rode a aplicação Slim
$app->run();
Slim Framework
<?php
$app->get('/users', function() use ($app) {
# Renderiza a view
$app->render('users.php');
});
$app->get('/users/:id', function($id) use ($app) {
$profile = ModelUsers::getUserByID($id);
# Renderiza a view com algum dado
$app->render('profile.php', array(
'profile' => $profile,
'view' => true
)
);
});
Slim Framework
<?php
/**
* Utilizando uma única rota para mapear múltiplos
* métodos HTTP usando a função map()
*/
$app->map('/users/validate', function() {
})->via('GET', 'POST');
Slim Framework
<?php
/**
* O Slim permite tratar mensagens de erro
* e enviar a resposta HTTP apropriada
*/
$app->get('/denied', function() use ($app) {
# Cria a mensagem de erro
$errorData = array('error' => 'Permission Denied');
# Retorna o status 403 do HTTP
$app->render('error.php', $errorData, 403);
# Podemos também enviar
$app->halt(403, 'Permission Denied');
});
Respect Framework

Respect Framework
- Micro-Framework para PHP 5.3+
- Criado por Alexandre Gaigalas (alganet)
- Mantido pela comunidade
- Instalável via PEAR ou Composer
Respect Framework
<?php
use Respect\Rest\Router;
$roteador = new Router;
$roteador->get('/users/*', function($userName) {
return 'Hello '. $userName;
});
Múltiplas rotas
<?php
$roteador->get(array('/user/*', '/users/*'), function($userName) {
return 'Hello '. $userName;
});
Rota Única
<?php
/**
* Utilizando uma única rota para mapear múltiplos
* métodos HTTP usando a função any()
*/
$roteador->any('/users/*', function($id) {
return "RestAPI";
/* Você pode usar o $_SERVER['REQUEST_METHOD']
* para verificar o tipo de requisição foi feita
*/
});
API de Cervejas
<?php
use Respect\Rest\Router;
use Respect\Config\Container;
use Respect\Validation\Validator as v;
use Respect\Relational\Mapper;
use Respect\Data\Collections\Collection;
/**
* Ler arquivo de configuração
*/
$config = new Container('config.ini');
/**
* Criar instância PDO com o SQLite usando as configs
*/
// diretório precisa ter permissão de escrita também
$mapper = new Mapper(new PDO($config->dsn));
// Criar instância do router
$router = new Router();
Módulos do Respect utilizados
-
Respect\Rest\Router;
-
Respect\Config\Container;
-
Respect\Validation\Validator as v;
-
Respect\Relational\Mapper;
-
Respect\Data\Collections\Collection;
<?php
// Rota para listar informações de uma cerveja ou todas
$router->get('/beers/*', function ($data) use ($mapper) {
// Validar com negação se string esta preenchida
if ( !isset($data) ) {
$cervejas = $mapper->cervejas->fetchAll();
header('HTTP/1.1 200 Ok');
return $cervejas;
}
// tratar os dados
$data = filter_var( $data, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// validar conteúdo
if ( v::not(v::alnum()->notEmpty())->validate($data) ) {
header('HTTP/1.1 404 Not Found');
return 'Não encontrada';
}
// buscar cerveja por id
if ( v::int()->validate( $data ) ) {
// buscar cerveja por id
$cerveja = $mapper->cervejas[$data]->fetch();
} else {
// buscar cerveja pelo nome
$cerveja = $mapper->cervejas(array( 'nome' => $data ))->fetch();
}
if ( !$cerveja ) {
header('HTTP/1.1 404 Not Found');
return 'Não encontrada';
}
header('HTTP/1.1 200 Ok');
return $cerveja;
});
<?php
$router->post('/beers', function () use ($mapper) {
//pega os dados via $_POST
if ( !isset($_POST) || !isset($_POST['cerveja']) || v::not(v::arr())->validate($_POST['cerveja']) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
// Validar o input
$validation = v::arr() // validar se é array
->key('nome', $rule = v::alnum()->notEmpty()->noWhitespace()) // validar a key 'nome' se não está vazia
->key('estilo', $rule) // utilizando a mesma regra da key de cima
->validate($_POST['cerveja']);
if ( !$validation ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
// tratar os dados
$cerveja = new stdClass();
$cerveja->nome = filter_var($_POST['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$cerveja->estilo = filter_var($_POST['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
// buscar cerveja pelo nome para ver se já existe
$check = $mapper->cervejas(array( 'nome' => $cerveja->nome ))->fetch();
if ( $check ) {
header('HTTP/1.1 409 Conflict');
return 'Cerveja já existe no sistema';
}
// gravar nova cerveja
$mapper->cervejas->persist($cerveja);
$mapper->flush();
// verificar se gravou
if ( !isset($cerveja->id) || empty($cerveja->id) ) {
header('HTTP/1.1 500 Internal Server Error');
return 'Erro ao inserir cerveja';
}
//redireciona para a nova cerveja
header('HTTP/1.1 201 Created');
return 'Cerveja criada';
});
<?php
$router->put('/beers/*', function ($nome) use ($mapper) {
//pega os dados
parse_str(file_get_contents('php://input'), $data);
if ( !isset($data) || !isset($data['cerveja']) || v::not(v::arr())->validate($data['cerveja']) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
// Validar o input
$validation = v::arr() // validar se é array
->key('nome', $rule = v::alnum()->notEmpty()->noWhitespace()) // validar a key 'nome' se não está vazia
->key('estilo', $rule) // utilizando a mesma regra da key de cima
->validate($data['cerveja']);
if ( !$validation ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
// tratar os dados
$nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// validar conteúdo
if ( v::not(v::alnum()->notEmpty())->validate($nome) ) {
header('HTTP/1.1 404 Not Found');
return 'Não encontrada';
}
// buscar cerveja pelo nome
$cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch();
if ( !$cerveja ) {
header('HTTP/1.1 404 Not Found');
return 'Não encontrada';
}
// tratar os dados
$newNome = filter_var( $data['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$newEstilo = filter_var( $data['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
//Persiste na base de dados ($mapper retorna objeto preenchido full)
$cerveja->nome = $newNome;
$cerveja->estilo = $newEstilo;
$mapper->cervejas->persist($cerveja);
$mapper->flush();
header('HTTP/1.1 200 Ok');
return 'Cerveja atualizada';
});
<?php
$router->delete('/beers/*', function ($nome) use ($mapper) {
// tratar os dados
$nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
// Validar com negação se string esta preenchida
if ( !isset($nome) || v::not(v::alnum()->notEmpty())->validate($nome) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
// verificar se existe a cerveja pelo nome
$cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch();
// BONUS - podemos buscar por id também
// $cerveja = $mapper->cervejas[$id]->fetch();
if ( !$cerveja ) {
header('HTTP/1.1 404 Not Found');
return 'Não encontrada';
}
$mapper->cervejas->remove($cerveja);
$mapper->flush();
header('HTTP/1.1 200 Ok');
return 'Cerveja removida';
});
Formatar resultado
<?php
$jsonRender = function ($data) {
header('Content-Type: application/json');
if ( v::string()->validate($data) ) {
$data = array($data);
}
return json_encode($data,true);
};
$router->always('Accept', array('application/json' => $jsonRender));
Benchmarks
-
Silex
- 2,533s por requisição
-
Slim
- 0,873s por requisição
-
Flask
- 0,567s por requisição
- Respect\Rest
- 0,395s por requisição
-
Restify (node.js)
- 0,231s por requisição
Saiba mais
- http://respect.li/
- https://github.com/Respect
- http://www.restapitutorial.com/
Dúvidas?
Curso Angularjs na Prática
- 24 horas
- Futuring Escola
(Próximo ao Parque Areião) - Sábados, de 4 de Outubro a 8 de Novembro
- R$ 395,00
http://futuring.com.br
Obrigado
Criando APIs com o Respect Framework
By Jean Lucas de Carvalho
Criando APIs com o Respect Framework
- 826