Serviços
API REST
Como funciona
A API REST tem um único ponto de entrada, /api (ApiController). Cada chamada é um POST com:
| Parâmetro | Conteúdo |
|---|---|
username, password |
Credenciais de um usuário do sistema (o tenant). |
action |
classe.método para o núcleo, ou plugins.<plugin>.classe.método. |
| o resto | Parâmetros da ação. |
curl -X POST https://meusite.com/api \
-d username=apiuser -d password=segredo \
-d action=usuarios.obtenerPerfilUsuario -d usr_codigo=12 -d language=pt
{ "status": "ok", "data": { "profile": { "…": "…" } } }
status |
Significado |
|---|---|
ok |
Sucesso; os dados estão em data. |
error |
Erro da operação (credenciais, validação, registro inexistente…). Inclui message. |
fail |
A chamada está malformada (ação inexistente, plugin inativo…). |
O parâmetro language muda o idioma das mensagens daquela chamada.
Resolução da ação
| Ação | Arquivo | Classe |
|---|---|---|
usuarios.loginUsuario |
src/api/usuarios.php |
UsuariosApiController::loginUsuario() |
plugins.noticias.manager.obtenerNoticias |
plugins/noticias/src/Api/manager.php |
ManagerApiController::obtenerNoticias() |
Só podem ser chamados métodos públicos declarados na própria classe, que deve estender RestProvider. Os nomes são validados como identificadores, então a ação não pode apontar para outro arquivo.
Chamadas em lote
action=batch.call executa várias ações em uma única requisição. bulk é um objeto (ou um JSON) com uma chave por ação e seus parâmetros; o que for enviado fora de bulk é compartilhado por todas. returnAs muda a chave do resultado (para chamar a mesma ação duas vezes).
curl -X POST https://meusite.com/api -d username=… -d password=… \
-d action=batch.call \
--data-urlencode 'bulk={"plugins.tablas.paises.obtenerPaises":{},
"plugins.noticias.manager.obtenerNoticias":{"limit":5,"returnAs":"ultimas"}}'
Escrever um endpoint
<?php
// src/api/productos.php -> ações productos.*
Koshkil::uses('sys.web.Rest.RestProvider');
Koshkil::usesModel('productos');
class ProductosApiController extends RestProvider {
// action=productos.listar&limit=10&offset=0&categoria=3
public function listar() {
$qb = TMProductos::where('usr_codigo', $this->apiUser()->usr_codigo)
->order('prd_nombre');
if ($categoria = $this->intParam('categoria')) {
$qb->where('cat_codigo', $categoria);
}
$total = $this->countRows(clone $qb);
$items = $this->paginate($qb)->getAsArray();
return $this->successResponse(['products' => $items, 'total' => $total]);
}
// action=productos.obtener&prd_codigo=15
public function obtener() {
$producto = TMProductos::where('prd_codigo', $this->intParam('prd_codigo'))
->where('usr_codigo', $this->apiUser()->usr_codigo)
->first();
if (!$producto) {
return $this->errorResponse('productos.not_found'); // chave do domínio "api"
}
return $this->successResponse(['product' => $this->onlyRequestedFields($producto->record())]);
}
}
Ajudas do RestProvider
| Método | Uso |
|---|---|
apiUser() |
Usuário autenticado (o tenant). Filtre sempre por ele. |
params(), param($k, $def) |
Parâmetros da chamada. |
intParam($k), arrayParam($k), intList($v) |
Parâmetros convertidos. |
esc($v) |
Escapa um valor para escrevê-lo dentro de SQL feito à mão. Não é necessário com where(), que já escapa. |
paginate($qb) |
Aplica limit e offset dos parâmetros. |
countRows($qb) |
Conta os registros de uma consulta. |
onlyRequestedFields($r) |
Filtra campos conforme shortVersion. |
translated($record), requestedLanguage() |
Conteúdo traduzível. |
findSubUser($valor, $coluna) |
Busca um subusuário do tenant. |
baseUrl(), absoluteUrl($caminho) |
URLs absolutas do site do tenant. |
successResponse($data, $msg) |
['status' => 'ok', 'data' => …] |
errorResponse($chave, $params) |
['status' => 'error', 'message' => …] (mensagem do domínio api) |
failResponse($chave, $params) |
['status' => 'fail', …] |
Uso interno, sem HTTP
O site pode consumir a própria API sem fazer uma requisição, com o ApiService. As credenciais do cliente ficam em config/domains/<domínio>/api.php:
<?php
$API = [
'default' => ['username' => 'apiuser', 'password' => '…'],
// 'mobile' => ['username' => '…', 'password' => '…'],
];
Koshkil::uses('sys.web.Rest.ApiService');
$api = ApiService::domain(); // cliente 'default' do domínio atual
$r = $api->call('plugins.noticias.manager.obtenerNoticias', ['limit' => 5]);
if ($r['status'] == 'ok') { $noticias = $r['data']['news']; }
$r = $api->batch([
'plugins.tablas.paises.obtenerPaises' => [],
'plugins.noticias.etiquetas.obtenerEtiquetas' => [],
]);
SuperController e FrontAjaxController já incluem o ApiAccessTrait:
$resposta = $this->api('usuarios.loginUsuario', ['subuser' => $email, 'pass' => $senha]);
$usuario = $this->apiData($resposta, 'users'); // data['users'], ou null se houve erro
Assim, o site e as aplicações externas usam exatamente a mesma lógica e as mesmas validações.