Overview

Skill Level: Beginner

Déployer une api REST PHP SLIM sur IBM Cloud

Ingredients

 

Step-by-step

  1. Base de données

    Réaliser la base de données patisserie comme indiqué dans ce mini guide :
    https://developer.ibm.com/recipes/tutorials/ibmcloud-mysql-france/

  2. Déploiement de l'API REST

    Cliquez sur le bouton Deploy to IBM Cloud. L’application va √™tre d√©ployer sur votre espace.
    https://github.com/cherryclass/api-gateau-slim

     

    api1

     

    Observez le fichier manifest.yml et composer.json. Toutes les dépendances vont être ajoutées lors du déploiement. Cette version de SLIM fonctionne uniquement avec PHP 7, IBM Cloud déploie pour le moment PHP 5. Il faut donc le spécifier dans composer.json.

    L’application¬†ne fonctionnera pas tant que le service de base de donn√©es de l’√©tape 1 n’est pas reli√©.¬†
    Dans le tableau de bord, cliquer sur l’api correspondante, puis sur connexions, cr√©er une connexion. Ajouter le service Mysql avec connect et reconstituer.

    On r√©cupere automatiquement les identifiants dans l’application. Vous pouvez observer la fonction connexion dans index.php.

    Il est possible de lier au d√©ploiement le service par la modification des lignes de commentaires dans le manifest.yml de l’application avec le nom de votre service, affich√© dans le tableau de bord.

     

  3. Test de l'application

    Testez l’URI gateau/8 de votre application. Attention √† bien remplacer l’url de l’application par la votre.
    Exemple¬†“http://api-gateau-palest-firetrap.mybluemix.net/gateau/8”¬†

    On pourra la tester directement via le navigateur puisque c’est une URI avec une m√©thode GET. On pourra √©galement tester les URI avec REASTEASY ou POSTMAN.

    Le serveur renvoie les éléments en JSON.

    api2

  4. Déploiement du client

    Cette √©tape peut √™tre test√©e directement sur votre poste, puisque le client n’utilise que du HTML et du Javascript. Il se connecte via AJAX au serveur.

    T√©l√©chargez les fichiers du d√©p√īt suivant :
    https://github.com/cherryclass/cli-gateau

    Modifiez la variable serveur de index.js avec l’URL correspondante de l’√©tape 2.
    Testez directement index.html sur votre poste, aucun serveur web n’est n√©cessaire.

     

    api3

     

    D√©ployez l’application gr√Ęce √† la commande :

     bx cf push

    Si vous ne savez pas comment déployer une application en ligne de commande, rendez-vous ici :
    https://developer.ibm.com/recipes/tutorials/ibmcloud-application-en-ligne-de-commande/

  5. Documentation et scalabilité

    Ajoutez le service auto-scaling à votre application.
    Ajoutez le service API Connect.

    Dans API Connect, s√©l√©ctionnez “importer¬†l’api √† partir d’un fichier ou d’une URL”, s√©l√©ctionnez URL puis ajoutez l’URL de votre application en ajoutant swagger.php de la forme :
    https://api-gateau.mybluemix.net/swagger.php

    Validez en haut √† droite, puis cliquez sur Explorer, brouillons en haut √† droite. On peut observer les diff√©rentes URI, les param√®tres etc. Ceci est obtenu par la g√©n√©ration d’un fichier Swagger en JSON.

    api4

  6. Authentification

    Nous allons utiliser les JSON Web Tokens de Firebase: https://jwt.io/
    Ce package est déjà installé dans cette application. On implémente deux URI :

    • token pour obtenir un token en fonction d’un mot de passe

    En cas de mot de passe correct, on retourne un token et un statut http valide, sinon un statut http unauthorized

    • validetoken indique si le token est valide

    En cas de token valide, on retourne un statut http valide, sinon unauthorized

     

    /**
    * @SWG\Get(
    * path="/token",
    * tags={"user"},
    * summary="Return a token",
    * description="Return a token",
    * @SWG\Parameter(
    * name="pass",
    * in="header",
    * required=true,
    * type="string"
    * ),
    * @SWG\Response(
    * response=200,
    * description="successful operation",
    * @SWG\Schema(
    * type="object"
    * )),
    @SWG\Response(
    * response=401,
    * description="unauthorized",
    *
    * ))
    */
    $app->get('/token', function(Request $request, Response $response){
    $pass = $request->getQueryParam('pass');
    $allowed= ckeckPass($pass);
    if($allowed){
    $token=getTokenJWT();
    return $response->withJson($token,200);
    }else{
    return $response->withStatus(401);
    }
    });


    /**
    * @SWG\get(
    * path="/validetoken",
    * tags={"user"},
    * summary="Test a token",
    * description="Test a token",
    * @SWG\Parameter(
    * name="token",
    * in="header",
    * required=true,
    * type="string"
    * ),
    * @SWG\Response(
    * response=200,
    * description="successful operation",
    *
    * ),
    * @SWG\Response(
    * response=401,
    * description="unauthorized"
    *
    * )
    * )
    * )
    */
    $app->get('/validetoken', function(Request $request, Response $response){
    $token = $request->getQueryParam('token');
    if(validJWT($token)){
    return $response = $response->withStatus(200);
    }else{
    return $response = $response->withStatus(401);
    }
    });

     

     

    Voici les fonctions¬†ckeckPass,¬†getTokenJWT et¬†validJWT. Il est pr√©f√©rable d’indiquer sa propre clef secrete.

     

     function getTokenJWT() {
    //30 min
    payload = array("exp" => time() + (60 * 30));
    return JWT::encode($payload, "myOwnKey");
    }
    function ckeckPass($pass){
    //$configs = include('config.php');
    //$sitepass=$configs["sitepass"];
    $sitepass="mypass";
    return (strcmp($sitepass,$pass)==0);
    }
    function validJWT($token) {
    $res = false;
    try {
    $decoded = JWT::decode($token, "superKey", array('HS256'));
    $res = true;
    } catch (Exception $e) {}
    return $res;
    }

     

     

    N’oubliez pas de tester ces URI avec REASTEASY. On peut modifier cette solution par un test selon login/mdp et un temps de jeton differenci√© selon le groupe de l’utilisateur.

    Modifiez les param√®tres de l’URI gateau¬†en POST qui permet d’ajouter un gateau. Cette URI doit tenir compte du token.

     

    $token = $request->getQueryParam('token');
    if(validJWT($token)){
    //insertion du gateau
    return $response->withStatus(200);
    }else{
    return $response->withStatus(401);
    }

     

    Modifiez le client en ajoutant une étape de saisie de mot de passe et récupération de token.

     

    $( "#btn-valide").click(function(event) {
    $.ajax({
    type: "GET",
    url: urlServeur+"/token",
    data: "pass="+ $("#password").val(),
    success: function(data){
    sessionStorage.setItem('token', data);
    //renvoie vers une autre page ou modification du contenu de la page en cours au choix
    window.location.href = "secondepage.html";
    },
    error: function(XMLHttpRequest, textStatus, errorThrown) {
    //on indique avec bootsrap une erreur sur le mot de passe
    $(".form-group-password").addClass("has-danger");
    $("#password").addClass("form-control-danger");
    }
    });
    });

     

     

    Puis pour l’enregistrement d’un gateau, n’oubliez pas de renvoyer le token.

     

    var htmlSession="<div class='container section2'><p> Session expirée <div class='offset-5 col-3'> 
    <a class='btn btn-danger' href='index.php' role='button'>Page principale</a> </div> </p> </div> ";
    var token=sessionStorage.getItem('token');
    $.ajax({
    type: "POST",
    contentType: 'application/json; charset=utf-8',
    url: urlServeur+"/gateau/"+id+"?token="+token,
    success: function(data){
    //gateau inseré
    },
    error: function(XMLHttpRequest, textStatus, errorThrown) {
    //erreur, mauvais token ou probleme à l'insertion à gérer selon le code HTTP !
    }
    });
  7. Exercices

    Implementez les URI suivantes en choisissant judicieusement la méthode :

    • gateaux qui permet d’afficher tous les gateaux
    • gateau/{id} qui supprime un gateau en fonction de son id avec gestion du jeton
    • gateau/{id} qui modifie le nom d’un gateau en fonction de son id¬†avec gestion du jeton

     

     

  8. Serverless

    A noter que nous n’utiliserons pas l’interface graphique qui pose probleme pour l’API.¬†
    R√©alisons l’URI gateau/{id} qui permet de r√©cuperer les informations d’un g√Ęteau. Nous allons l’adapter en passant l’id non plus dans l’URI mais en param√®tre.

     

    1)¬†Cr√©ation de l’action

    Enregistez ce code dans un fichier gateau.php en modifiant l’host et le mot de passe correspondant.

    <?php
    function main(array $args)
    {
    $id = $args["id"]?? 1;
    $sql = "SELECT * FROM gateau WHERE id=:id";
    $dsn='mysql:dbname=patisserie;host=sl-us-south-1-portal.9.dblayer.com:55';
    $user='admin';
    $password='AA';
    try {
    $dbh = new PDO($dsn,$user,$password);
    $statement = $dbh->prepare($sql);
    $statement->bindParam(":id", $id);
    $statement->execute();
    return $result = $statement->fetchObject();
    } catch (PDOException $e){
    return "error";
    }
    }

     

    Executez la ligne de commande¬†bx wsk action create getgateau gateau.php –web true

     

    2)¬†Liaison de l’action √† l’API

    Executez bx wsk api create /patisserie /gateau get getgateau –response-type json

    Nous obtenons l’URL complete :
    https://service.us.apiconnect.ibmcloud.com/gws/apigateway/api/b55530a2f02dba24e86a6b3166a376a80d0a20985795209d1d02508fccec5580/patisserie/gateau

    Il suffit de passer en parametre l’id avec RestEasy par exemple

    a

     

    3) Liaison du service SGBD √† l’action

    Executez la commande :
    bx wsk service bind compose-for-mysql getgateau

    Le service de base de donn√©es est maintenant li√© √† l’action. On peut donc modifier la connexion.

    <?php
    function main(array $args)
    {
    $id = $args["id"]?? 1;
    $sql = "SELECT * FROM gateau WHERE id=:id";
    $vcap_services = $vcap_services = $args["__bx_creds"];
    $db_creds = parse_url($vcap_services['compose-for-mysql']['uri']);
    $dbname = "patisserie";
    $dsn = "mysql:host=" . $db_creds['host'] . ";port=" . $db_creds['port'] . ";dbname=" . $dbname;
    try {
    $dbh = new PDO($dsn, $db_creds['user'], $db_creds['pass'],array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));
    $statement = $dbh->prepare($sql);
    $statement->bindParam(":id", $id);
    $statement->execute();
    return $result = $statement->fetchObject();
    } catch (PDOException $e){
    return "error";
    }
    }

     

    Mettez √† jour l’action :
    bx wsk action update getgateau gateau.php –web true

     

     

    On utilise ici directement OpenWhisk. Nous ne g√©rons pas les instances et n’installons pas de¬†micro-framework. La consommation de ressource est √©galement beaucoup plus faible. Elle n’est toutefois pas nulle.¬†

    Vous trouverez ici comment d√©finir des URI avec d’autres m√©thodes,POST, DELETE etc :
    https://console.bluemix.net/docs/openwhisk/openwhisk_apigateway.html#openwhisk_apigateway

     

  9. Kits de démarrage web

    Je vous invite √† tester les kits de d√©marrage des applications web. Il n’y a pour le moment pas de kit avec PHP mais Express.js et Flask par exemple.

  10. What else ?

    Ces tutoriels peuvent vous intéresser :
    https://developer.ibm.com/recipes/author/l-frebourg/

Join The Discussion