Client HTML5 Web Service RESTFull [Suite]

De $1

Dans le TP d'aujourd'hui, vous allez raffiner à la fois l'application cliente et le services web.  Jusqu'à présent la plupart de vos méthodes ne renvoient pas de réponse à l'application cliente, vous n'avez pas gérer les exceptions et traiter les erreurs.  Dans un premier temps, vous allez définir un format standard de réponse pour les requetes clientes et appliquer ce format aux réponses renvoyées au client.

  1. Le format de toutes les réponses doit ressembler à 
{
    "status":true,

    "message":"Liste des catégories",

    "data":         [{

        "id":1,

        "nom":"Mathématiques",

        "description":"Description"

        }]
}

Dans cet exemple,

Status indique au client le statut de la réponse; true indique que tout s'est bien passé et false indique qu'une erreur s'est produite.  N.B: On ne peut pas forrcément se baser sur le statut de la réponse HTTP car une requête HTTP peut bien aboutir sans pour autant que la demande du client soit compatible avec la logique de votre application.  Exemple: la liste des catégories est vide.  

Message : est le message associé au statut.  Par example si le status est false (la liste est vide); ce message peut etre "Il n'y a pas de catégories définies". 

Data : renferme les données envoyées au client. 

Pour ce faire, vous allez créer une classe générique avec trois attributs (boolean status, String message, T data). T permet de définir un objet de type générique. 

public class Message <T> {

    public boolean status;

    public String message;

    public T data;

}
NB: Modifiez la class ApplicationConfig pour indiquer à JAX-B d'utiliser Jackson pour la serialization 
@Override

    public Set<Class<?>> getClasses() {

        Set<Class<?>> resources = new java.util.HashSet<Class<?>>();

        // following code can be used to customize Jersey 2.0 JSON provider:

        try {

            Class jsonProvider = Class.forName("org.glassfish.jersey.jackson.JacksonFeature");

            // Class jsonProvider = Class.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature");

            // Class jsonProvider = Class.forName("org.glassfish.jersey.jettison.JettisonFeature");

            resources.add(jsonProvider);

        } catch (ClassNotFoundException ex) {

            java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);

        }

        addRestResourceClasses(resources);

        return resources;

    }
Modifiez la signature de la méthode list de la classe CategorieRessource pour renvoyer un objet de type Message au lieu d'une liste de catégories. 
  • status=true; Si la liste est non vide  
  • status=false; Si la liste est vide 
  • message sera défini en fonction du statut de la liste (ou éventuellement des erreurs) 
  • data contiendra les données de la réponse

Modifiez maintenant l'application cliente pour prendre en compte cette nouvelle représentation de données. Pour cela, vous allez devoir modifier la méthode getData de categorie.js (si vous l'aviez pas changé bien sur)

var getData = function() {
    $.ajax({
        url: "http://localhost:8080/bibliotheque_ntdp/webresources/category",
                type: "GET",
        headers: {
            Accept: "application/json"
        }
    }).success(function(data, status, jq) {
   //Cette fonction indique à knockout d'appliquer les données aux éléments de la page 
    //Elle est toujours appelée quand les données sont pretes et est appelée qu'une fois 
    if(data.status)    
     ko.applyBindings(new ViewModel(data.data));
    else{
        alert(data.message)
        }
    }).error(function(jq, status, error) {
        $(".error").text(JSON.stringify(status + " " + error));

    });
};

Répétez les étapes précédentes pour toutes les actions du Web Service et adaptez vos interfaces en conséquence.

 Utilisation des headers 

Ajoutez une action login à votre Web Service prenant en paramètre un nom d'utilisateur et un mot de passe (ces paramètres doivent etre envoyées dans le corps de la requete et non dans l'URL).   Si la connection a réussi, renvoyez les informations de l'utilisateur (sans le mot de passe) et un code d'authorisation que vous générerez automatiquement.  N.B: il faut modifier l'entité Users pour y ajouter un attribut authorizationKey.  L'authorizationKey est généré puis stocké dans le champ correspondant à l'entité user dans la base de données. 

Ajouez une page (connexion.html) dans l'application web pour permettre à un utilisateur de se connecter à l'application.  La réponse à l'action login contiendra un utilisateur et sa clef d'authorisation.  Sauvegardez la clef d'utilisage dans la session storage du navigateur de telle sorte à pouvoir la réutiliser pour les prochaines requêtes au WS. 

Sécurisez les actions de modification dans le Web service (Create, Update et Delete).  Si un utilisateur n'est pas connecté, ces actions lui seront refusées. 

Une fois l'utilisateur connecté, pour toutes nouvelles requêtes vers les actions sécurisées il faudra ajouter la clef d'authorization qui vous a été renvoyée lors de la connexion de l'utisateur.  Dans ces méthodes, il faut vérifier: 

  • Que la clef d'authorisation est présente 
  • Que cette clef corréspond à un utilisateur de la base 

Si ces conditions ne sont pas vérifiées, renvoyez un message d'erreurs à l'utilisateur lui invitant à se connecter. 

La clef à ajouter dans l'en tete de la requete est : miage_authorization 

Pour ajouter une clef dans l'entete d'une requete : 

$.ajax({

        url: "http://localhost:8080/bibliotheque_ntdp/webresources/category",

                type: "GET",

        headers: {

            Accept: "application/json", 

            miage_authorization: "12345678903"

        }

    }).success(function(data, status, jq) {

    }).error(function(jq, status, error) {    });

Il faut aussi modifier la classe RequestFilter pour authoriser l'envoie de cette nouvelle clef dans l'entete des requtes Ajax. 

response.getHeaders().putSingle("Access-Control-Allow-Headers", "content-type, miage_authorization");

Pour lire des paramères dans l'entête d'une requête à partir d'une action du WS, inspirez-vous  de cet exemple. 

@POST 	@Path("/login") 	
public Response login(@HeaderParam("miageauthorization") String miageauthorization) {         
	//print the key          
	System.out.println(miageauthorization);          		
	return Response.status(200) 			
	.entity("Login is called with : " + miageauthrization) 			
	.build(); 	
}