Salta el contingut

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:

  1. Crear una carpeta API-client

  2. 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

  1. 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

  1. Per provar-ho, Amb el navegador URL: http://localhost/client.php

O la ruta on hi tingueu la pàgina API-client/client.php

Guzzle exemple
    <?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

https://curl.se/windows/

També podem trobar algunes eines online que ens poden ajudar a testejar o bé a crear la comanda

https://curlbuilder.com/

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é

Httpie

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.

JWT

Trobareu la documentació del JWT aquí

Trobareu les llibreries per generar el JWT aquí

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

configuracio-bdd.php
    <?php
        $usuari = "user";
        $pass = "1234";
        $bdd = "database";
        $servidor = "localhost";
    ?>

I al codi fariem alguna cosa similar a:

index.php
    <?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:

        composer require vlucas/phpdotenv   
    
  • Des de php

        <?php
        require 'vendor/autoload.php';
    
        $dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
        $dotenv->load();
    
        echo "Usuari: " . $_ENV['DB_usuari'] . "<br>";
        echo "Contrasenya: " . $_ENV['DB_pass'];
        ?>
    

Ja només ens resta de validar que aquest fitxer .env està al .gitignore

.gitignore
    # Ignorar fitxers d'entorn
    .env