Angular/Ionic: simuler un serveur avec des fichiers JSON

Je travaille actuellement sur un projet mobile Ionic et j’ai du mettre en place un mode démonstration avec des données fake. Au lieu de faire un appel au serveur, j’utilise des fichiers JSON qui contiennent les données à retourner.

Cela peut également être pratique si vous devez travailler sans être dépendant de la partie serveur.
Afin d’éviter de toucher le code métier (mes services angular) et faire des tests dans mes controllers/services pour savoir si je suis en mode démo ou pas, j’utilise un interceptor angular (la documentation angular sur $http et les interceptors) qui va faire la requête http ou renvoyer les bonnes de démonstration.

L’astuce ici consiste à contourner le cache de $http. Il faut modifier dans la fonction request de l’interceptor l’objet config pour définir une fonction get dans le cache. (Seulement les verbes HTTP GET et JSONP peuvent utiliser le cache $http. Je force un appel en GET mais garde en mémoire la méthode d’origine). Ainsi, l’appel serveur ne sera pas fait et la fonction définie en tant que cache sera appelée. Vous pouvez ainsi retourner vos données, dans notre cas à partir de fichiers json.

Voici le code de mon interceptor:

(function (module) {
  'use strict';
  module.factory('demoInjector', ['baseUrl', '$injector', function(baseUrl, $injector) {

    //check if request is done on backend and not templates for example 
    const isBackendRequest = function (config) {
      return config.url.indexOf(baseUrl) > -1;
    };

    var sessionInjector = {
      request: function(config) {
        if (!isBackendRequest(config)) {
          return config;
        }
        const demoService = $injector.get('DemoService');
        if (demoService.isDemoMode) {
          //store http method and change request method to GET because only GET and JSONP are cached
          config.originalMethod = config.method;
          config.method = 'GET';
          config.cache = {
            get: function() {
              return demoService.mock(config);
            }
          };
        }
        return config;
      }
    };
    return sessionInjector;
  }]);

  module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.unshift('demoInjector');
  }]);
})(angular.module('diagral'));

Dans le demoService, service qui contient la logique du mode démonstration, on se base sur l’url (via une RegExp) et la méthode HTTP (sauvegardée dans la propriété originalMethod via l’interceptor) de l’objet config pour déterminer le contenu approprié.

Voyons maintenant comment charger un fichier JSON inclus dans votre projet Ionic (en version 1). Pour ma part, les fichiers sont stockés dans /www/demo.
Sous Android, le dossier www est placé dans le dossier android_asset. Il faut donc adapter l’url du fichier cible en prenant compte de la platform cible via ionic.Platform. Il suffit ensuite de faire une requête via $http avec le chemin du fichier.

Les exemples ci-dessous sont en ES2015 avec des classes (d’où les this …)

this.url = '';
if (ionic.Platform.isWebView() && ionic.Platform.isAndroid()) {
    this.url = '/android_asset/www/';
}

this.$http.get(this.url + 'demo/myFile.json');

Afin de simuler un temps d’attente réaliste, vous pouvez utiliser $timeout.

const delay = 1000; //délai d'1 seconde
const responseDeferred = this.$q.defer();
this.$timeout(() => {
  this.$http.get(this.url + 'demo/myFile.json')
    .then(resp => responseDeferred.resolve(resp));
}, delay);
return responseDeferred.promise;

Il est également possible de renvoyer un objet JavaScript directement (si vous avez besoin de gérer un état par exemple). Il faut, dans ce cas, créer une réponse comme ceci:

const response = {
  config: config,
  status: 200,
  headers: () => {},
  data: () => angular.toJson({
          name: 'Julien',
          age: 35,
          lastUpdate: Date.now()
        })  
};

Voici le code final de la fonction mock qui utilise un tableau de configuration contenant les mocks avec la réponse attendue et les différentes options (délai, json/fonction, …).

mock(config) {
  const response = {
	config: config,
	status: 200
  };

  //Configuration du tableau des mocks
  const staticResponses = [
  {
    pattern: /login$/,
    jsonFile: 'login.json',
    method: 'POST'
  },
  {
    pattern: /article$/,
    data: () => this.getFakeArticles(),
    delay: 500
  },
  //...
  ];
  const staticResponse = this.findMock(staticResponses, config);
  if (staticResponse) {
    const r = this.$q.defer();
    const { delay = 200 } = staticResponse; //200ms by default if not defined
    this.$timeout(() => {
      if (staticResponse.jsonFile) {
        this.$http.get(this.url + 'demo/' + staticResponse.jsonFile)
          .then(resp => r.resolve(resp));
        return;
      }
      if (this._.isFunction(staticResponse.data)) {
        response.data = staticResponse.data(config.data, config.url);
        r.resolve(response);
        return;
      }
    }, delay);
    return r.promise;
  }

  response.status = 500;
  response.data = { message: 'Indisponible en mode démo'};
  return this.$q.reject(response);
}

findMock(staticResponses, { url: requestUrl, originalMethod: method}) {
  return this._.find(staticResponses, r => r.pattern.test(requestUrl) && method === (r.method || 'GET'));
}

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *