具有多个API数据服务的应用程序

Application with multiple API data services

本文关键字:服务 应用程序 数据 API      更新时间:2023-09-26

我正在尝试设计一个Angular应用程序,它的组件注册了一个回调函数和一个'数据请求对象'与Angular服务。该服务基本上跟踪所有数据请求对象以及它们引用的回调函数。然后,它执行长轮询以对RESTful API进行异步调用。当所有数据都传入时,服务决定哪些组件需要哪些数据片段,并使用API调用的结果调用它们。

我有麻烦的问题是,每个组件可能需要将数据"转换"成规定为特定格式的JSON对象。例如,图表组件可能要求数据结果以一种方式显示,但表组件可能要求数据结果以另一种方式显示。

为了使事情变得更加复杂,我想以这样一种方式设计我的数据服务,即组件可以注册来自多个不同的RESTful api的数据。

由于我对Angular相当陌生,所以我想获得一些关于完成这类应用的最佳实践的帮助。我想做的是有一个"主"数据服务,我的组件注册。注册函数将接受一个回调函数和一个数据请求对象作为参数,格式如下:

{
    "service": "apiService",
    "request": {
        ...
    }
}

那么每个RESTful API都会有一个单独的Angular服务。这些子服务将处理如何处理数据请求对象,并对应于上面的"service"字段。然后,主数据服务将在一个较长的轮询周期中将请求排队到子服务。所以主数据服务看起来像这样(注意:我使用的是ES6):

class DataService {
  constructor($q, $interval, $injector) {
    this.$q = $q;
    this.$interval = $interval;
    this.$injector = $injector;
    this.subscriptions = [];
    this.callbacks = [];
    this.poll();
  }
  poll() {
    let reqs = [];
    angular.forEach(this.subscriptions, (sub, i) => {
        let service = this.$injector.get(sub.service);
        let deferred = this.$q.defer();
        reqs.push(deferred);
        service.get(sub.request).then((result) => {
            this.callbacks[i](result);
            deferred.resolve(result);
        }, (result) => {
            deferred.reject(result);
        });
    });
    this.$q.all(reqs).then(() => {
        this.$interval(poll, this.pollInterval || 10000);
    });
  }
  register(request, callback) {
    this.subscriptions.push(request);
    this.callbacks.push(callback);
  }
}
angular.module('DataService', []).service('DataService', DataService);

我有麻烦弄清楚如何实现的那块是"数据转换"块。据我所知,实际上只有两个地方可以看到这个数据转换发生:

  1. 组件内部
  2. 单个API服务内部

第一种方式对我来说似乎不是一个可行的选择,因为它打破了组件应该有点"愚蠢"的惯例。组件不应该处理转换RESTful API返回的数据:它应该按原样使用数据。

但是第二种方法也带来了另一个问题,那就是每个RESTful API服务都必须为我创建的每个组件类型具有转换函数。这在我看来似乎不"干净"。

是否有其他方法可以设计我的应用程序来实现这个目标?

一个建议:使用angular内置的事件系统。

//Constructor
constructor($q, $interval, $injector, $rootScope) {
 this.$q = $q;
 this.$interval = $interval;
 this.$injector = $injector;
 this.$rootScope = $rootScope;
 this.listeners = [];
 //No idea how its written in Angular2 but cleanup the event listeners when this is destroyed
 //Example in 1.5.x:
 //$scope.$on('$destroy', () => angular.forEach(this.listeners, l => l());
 this.poll();
}
//The new register method
register(ev, callback) {
 //When cleaning up - iterate over listeners and invoke each destroy function - see constructor
 this.listeners.push(this.$rootScope.$on(ev, callback));
}
//The new poll method
poll() {
    let reqs = [];
    angular.forEach(this.subscriptions, (sub, i) => {
        let service = this.$injector.get(sub.service);
        let deferred = service.get(sub.request).then((response) => {
         //responseToEventsMapper should map and parse response to list of events to fire.
         //Lets say the response is an authentication response for login attempt,
         //then the triggered events will be 'Auth:stateChange' and 'Auth:login' with
         //response.user as data. configure responseToEventsMapper upfront.
         let events = this.responseToEventsMapper(response);
         angular.forEach(events, event => this.$rootScope.$emit(event.type, event.data));
        });
        reqs.push(deferred);
    });
    this.$q.all(reqs).then(() => {
        this.$interval(poll, this.pollInterval || 10000);
    });
  }