WS: Com ho fem per...?¶
Es tracta d'usar la nostra API com a backend de les aplicacions.
Com fer que sempre s'executi el controlador server.php¶
A dins de la carpeta a on hi ha la API, normalment farem /API. Tindrem l'arxiu .htaccess, amb:
RewriteEngine On
RewriteBase /API
options +FollowSymLinks
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* server.php/$0 [L]
Per tant:
- RewriteBase /API: Ruta a on hi tenim la API per indicar el WebBASE
- RewriteRule .* server.php/$0 [L]: arxiu controlador que executarem. Podem tenir diverses versions i anar canviant en fase de desenvolupament.
Cridem a la API des de frontend¶
Serà l’acció més usada per fer les crides a la API desde el frontend per obtenir els valors directament desde la nostra API pròpia o tercers, així no caldrà trànsit desde el nostre servidor.
Cal anotar que en aquest cas pot quedar exposada la nostra API-KEY que caldria enviar per que estaria en el codi del navegador que ha rebut.
Hi ha diverses possibilitats d’ús: AJAX, fetch, AXIOS, … +info
En el nostre cas de frontend un exemple amb AXIOS podria ser:
llistaArticles()
{
axios.get("https://api.mevaApp.com/llistaarticles")
.then(res => this.articles = res.data)
}
On fariem una crida a la API amb la URL https://api.mevaApp.com/llistaarticles
Notar que AXIOS cal de llibreria específica per usar-lo, en canvi fetch és natiu de javascript.
amb Fetch podria ser:
//exemple de fer crida PUT
$data = '{"name":"Maria", "address":"Migdia, 25", "url":"http://carles.cat"}'
fetch('http://localhost/API/contactes/Maria',{method:'PUT', body:$data})
.then(response => response.json())
.then(data => console.log(data));
Cridem a la API des de backend¶
Es tracta de poder cridar (consumir) des de php una API-REST, pròpia o no
Usarem una llibreria Guzzle per a poder usar com a client. Per fer-ho seguim les pases:
-
Crear una carpeta API-client
-
Des de la consola la instal·lem amb el composer:
- composer init
- composer require guzzlehttp/guzzle
Si esteu amb entorn windows podeu mirar a: https://www.hostinger.es/tutoriales/como-instalar-composer/#2-Instalando-Composer-en-Windows
- Una vegada instal·lada ja podem crear el client que consumeixi la API-REST creada.
- indicant la URL de la API-REST
- Temps d'espera
- Capturem el retorn si el codi és 200
Crear un fitxer client.php a la carpeta API-client i copieu el codi inferior
- Per provar-ho, Amb el navegador URL:
http://localhost/client.php
O la ruta on hi tingueu la pàgina API-client/client.php
<?php
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;
//servei de crida a la API
// base_uri -> URL de la API
// timeout --> temps d'espera
$client = new Client([
'base_uri' => 'http://localhost/API/clients’,
'timeout' => 5.0,
]);
//use mètode GET
$res = $client->request('GET');
if ($res->getStatusCode() == '200') //Si retorna 200 = OK
{
// mostrem retorn
echo $res->getBody();
}
?>
Com fem test de la nostra API¶
Per a poder testejar la nostra API REST feta amb PHP podem usar la eina CURL
Aquesta eina ens permetrar realitzat petions en línia de comandes poden usar tots els mètodes disponibles
curl https://www.example.com/
curl -d "name=Rafael%20Sagula&phone=3320780" http://www.where.com/guest.cgi
Podeu veure molts més aquí
També us podeu instal·lar curl per a windows
També podem trobar algunes eines online que ens poden ajudar a testejar o bé a crear la comanda
També existeix la possibilitat d’incorporar-ho a PHP i poder fer crides directament des de la banda del servidor
https://www.php.net/manual/en/book.curl.php
Podem accedir a l'eïna Postman
Postman *Cal registrar-se!
O També
Com permetre accés a la API des de un altre orígen¶
Per seguretat el servidor web bloqueja l’execució de codi php si detecta que l’origen de la petició HTTP no prové del mateix servidor. En el cas d’ús de les APIS això pot no ser així en molts casos, per tant, cal obrir aquesta possibilitat. Tot i que això significa que obrim una finestra a atacs massius.
Tenim el concepte Esquema:
- Protocol
- Domini
- Port
Per exemple:
- http://p1.daw.com/API
- http://172.17.100.115/API
Encara que el domini i la IP siguin el mateix resolt pel DNS, ja no és el mateix esquema.
Una manera clara d'entendre el cas i poder-ho provar en local seria:
- Tenim un servidor web Apache a localhost (127.0.0.1)
- Tenim un index.html amb una crida AJAX a nosaltres mateixos: http://localhost/API/Clients/Modificar`
- Fem una càrrega de la pàgina inicial a: http://127.0.0.1/index.html
- Salta el CORS i bloqueja la crida a la funció
Per configurar-ho cal modificar la capçalera Access-Control-Allow-Origin.
Al fitxer php posarem a l’inici, per exemple:
header('Access-Control-Allow-Origin: *');
Això permetria l’accés des de qualsevol origen
Les opcions són:
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: <origen>
Access-Control-Allow-Origin: null
Per tant podem arribar a indicar un sol origen si s’escau:
header('Access-Control-Allow-Origin: https://www.mevaAplicacio.com');
També podem controlar l’accés als mètodes permesos amb:
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Methods: *');
On posarem un llistat dels mètodes permesos, els que no hi siguin, no ho serà.
Per altra banda, cal tenir present que ens pot passar que l'identificador de la sessió al servidor sigui diferent que no poguem recuperar les variables '$_SESSION["xxx"]';
Si fem un 'GET', 'HEAD' o 'POST', fa la crida directe, en altre cas realitza un 'preflight' i caldrà tenir preparat el codi de la APIRest per activar-ho:
<?php
...
if ($metode == "OPTION")
{
header('HTTP/1.1 200 OK');
exit();
}
...
?>
Com recuperar les funcions en la ruta¶
A dins de la funció Serve() de la classe, tenim:
$paths = explode('/', $this->paths($uri));
array_shift($paths);
$resource = array_shift($paths);
private function paths($url)
{
$uri = parse_url($url);
return $uri['path'];
}
Això ens permetrà tenir el valor de la 1a part de la ruta.
IMPORTANT !
Caldrà analitzar el format de la ruta i fer tant array_shift com faci falta per anar extraient la part que no ens cal.
Per tant podrem executar la funció associada
if ($resource == “llistaArticles” )
{
// codi associat a listar articles
}
else
{
header('HTTP/1.1 404 Not Found');
}
Això podria correspondre a una crida similar a:
http://www.lanostraapi.com/api-v2/llistaArticles
En aquest cas de l’exemple, en cas que l’acció indicada a la ruta no sigui la de “LlistaArticles”, l’API retornarà un codi d’estat de pàgina no trobada.
Com retornar l'estat d'execució¶
Bàsicament per finalitzar l’execució de la lògica d’una crida a una funció de la API el que fem és
header('HTTP/1.1 401 Unauthorized');
Amb la possibilitat de triar un dels estats següents
Aquesta informació és independent de les dades que retornarem per JSON
Per tant, podem usar aquesta lògica per exemple:
- execució correcte de la API: 200 OK
- accés no autoritzat: 401 Unauthorized
- pàgina no trobada: 404 Not Found
- mètode no permés: 405 Method Not Allowed
El text mostrat a continuació del valor de l'estat es podrà recollir al frontend.
Un exemple pràctic d'un endpoint sobre /Clients/Modificar
que el tenim definit per usar com a mètode POST
i rebem un altre mètode fariem:
<? php
...
$metode = $_SERVER['REQUEST_METHOD'];
if ($metode == "POST")
{
// accions a realitzar
...
}
else
{
header('HTTP/1.1 405 Mètode no permès');
}
Com obtenir dades JSON¶
Per obtenir dades JSON enviades a un endpoint d'una API, per exemple per POST, farem:
$data = json_decode(file_get_contents('php://input'));
A $data tindrem les dades que hem passat amb JSON ja decodificades.
Ara podrem accedir a elles, per exemple:
$data->nom;
$data->edat;
Aquestes dades les podem enviar per la capçalera d'una crida AJAX amb fetch de la següent manera:
// fem crida PUT per passar valors com a dades JSON
const user = {username,password};
fetch(url,{method:"PUT", body:JSON.stringify (user)})
Com retornar dades JSON¶
Per retornar dades JSON ens cal indicar dues coses:
-
el format del que retornem
header('Content-type: application/json');
-
les dades pròpiament amb format JSON
echo json_encode($resposta);
Per exemple en la funció de retornar tots els contactes seria:
header('Content-type: application/json');
$resposta = null;
$SQL = 'SELECT * FROM contactes';
$consulta = (BdD::$connection)->prepare($SQL);
$qFiles = $consulta->execute();
if ($qFiles > 0)
{
$consulta->setFetchMode(PDO::FETCH_ASSOC);
$result = $consulta->fetchAll();
foreach($result as $fila)
{
$resposta[] = $fila;
}
}
echo json_encode($resposta);
Com generem un Token JWT¶
JWT és un estàndar obert que ens permet usar un token en format JSON per a un ús segur d'una API.
l'ús més habitual és per autoritzar l'accés a les funcions i dades que ens ofereix la nostra API.
Normalment obtindrem el Token JWT una vegada ens hem autenticat o iniciat sessió al web(intranet).
L'estructura d'aquest token és, format JSON codificat:
Capçalera.Dades.Signatura
ON:
Capçalera:
{
"alg": "HS256",
"typ": "JWT"
}
Dades:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Signatura: (creat segons)
HMACSHA256(
base64UrlEncode(capçalera) + "." +
base64UrlEncode(dades),
secret)
La Signatura serveix per verificar que les dades no s'han modificat pel camí.
Donaria un JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Les dades que és desen al payload
no han de ser crítiques i només haurien de servir per a ser identificats a dins del endpoint de l'API.
Per exemple:
{ "id": 33 }
D'aquesta manera podrem recuperar el concepte de SESSIO
a dins del backend.
Important
Recordem que una API Rest, és statless
que significa que el servidor no recorda ni resgistra cap mena d'informació de la sessió de navegació de les peticions HTTP als endpoint per part del client.
Per tant, la funció del JWT que es genera al servidor i s'envia com a resposta al client implementarà aquesta funció.
Secret
Notem que la informació que es desa al token està codificada, que no xifrada, i aquesta pot ser decodificada amb la pròpia informació que conté la capçalera.
Per tant, restringirem la validesa de que la informació és vàlida a dos conceptes:
- secret: Paraula reservada només a la part del backend que s'usarà a la crida de la validació del token
- expiració: Data d'expiració del token mentre s'admet com a vàlid.
Per instal·lar la llibreria
composer require firebase/php-jwt
Per usar-la a dins del PHP
namespace Firebase\JWT;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
require_once('./vendor/autoload.php');
Per generar el jwt
ho farem:
// clau secreta
private $key = 'example_key';
// dades
$payload = [
'iss' => 'http://example.org',
'aud' => 'http://example.com',
'iat' => time(),
'nbf' => time(),
'exp' => time() + 30, //token vàlid durant 30 segons
'sub' => "Carles"
];
$jwt = JWT::encode($payload, $key, 'HS256');
Per validar el jwt
ho farem
$key = 'example_key'; // clau secreta
$dades = json_decode(file_get_contents('php://input')); // obtenim dades enviades amb format json
$jwt = $dades->token; // extraiem el token
$decoded = JWT::decode($jwt, new Key($key, 'HS256')); // processem token
$usuari = $decoded->sub; // obtenim la dada d'usuari que l'hem passat.
Com protegim dades connexió BdD a .env pel git¶
Normalment tenim les dades de connexió a la BdD a dins d'un fitxer PHP
de l'estil
<?php
$usuari = "user";
$pass = "1234";
$bdd = "database";
$servidor = "localhost";
?>
I al codi fariem alguna cosa similar a:
<?php
include "configuracio-bdd.php" // configuració BdD
include "bdd.php" // Classe BdD
BdD::connect($servidor,$usuari,$pass,$bdd);
?>
Aquest arxiu quedarà exposat a dins dels arxius que pujarem al fer
git push
per tant pot ser bona pràctica usar un arxiu .env
DB_usuari = "user"
DB_pass = "1234"
DB_bdd = "database"
DB_ ervidor = "localhost"
I per a poder-hi accedir des de el PHP caldrà:
-
Instal·lar la llibreria:
-
Des de
php
Ja només ens resta de validar que aquest fitxer .env
està al .gitignore