【问题标题】:Cache results of API calls inside Electron application在 Electron 应用程序中缓存 API 调用的结果
【发布时间】:2021-12-05 21:46:15
【问题描述】:

我正在使用 Electron 编写一个桌面应用程序来封装和提供 Angular 文件。 Electron 只需启动一个 chrome 网络浏览器,它会加载 .js 文件并执行 Angular 代码,然后对 Electron 进行回调以操作本地文件。

Angular 端多次调用我不拥有的公共 API 来检索有关用户想要操作的文件的元数据。此 API 提供的内容仅每 24 小时左右更新一次,但 x-cache 标头似乎只持续几秒钟,即使数据很少更改。这意味着我的应用可能会向速率受限的 API 发出大量不必要的请求。

我想保存 API 调用的响应某处,最好是在 Node/Electron 方面(但如果这是唯一的选择,也很高兴在 Angular 方面),然后返回这些数据而不是调用速率受限的 API,但我不知道如何去做。似乎我需要某种中间件/代理,但我所有的搜索都没有找到我要找的东西。

当浏览器最终发出 http 请求时,如何在此应用程序中缓存它们?

【问题讨论】:

    标签: javascript node.js angular caching electron


    【解决方案1】:

    我想在 Electron 端这样做的主要原因是我希望缓存在应用程序运行之间保持不变。再想一想,我意识到这是完全没有必要的,因为浏览器有多种持久化数据的方式。

    我的解决方案使用了 HttpInterceptor 和 CacheService found in this answer 的修改版本,但将数据持久化到 localStorage。

    http-interceptor.service.ts:

    import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { Observable, of } from 'rxjs';
    import { tap } from 'rxjs/operators';
    import { CacheService } from './cache.service';
    
    @Injectable({ providedIn: 'root' })
    export class CacheInterceptor implements HttpInterceptor {
        constructor(private readonly cacheService: CacheService) {}
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
            if(req.method !== 'GET') {
                return next.handle(req);
            }
            const cachedResponse: HttpResponse<any> = this.cacheService.get(req);
            if (cachedResponse) {
                const response = new HttpResponse({ body: cachedResponse });
                return of(response);
            } else {
                return next.handle(req)
                    .pipe(tap(stateEvent => {
                        if (stateEvent instanceof HttpResponse) {
                            this.cacheService.set(req, stateEvent.clone());
                        }
                    })
                );
            }
        }
    }
    

    cache.service.ts - 为了确保随着缓存大小增加的响应能力,我只在缓存初始化时从 localStorage 加载值,否则只从应该是序列化的副本的map 中读取本地存储中的数据。每次set 调用后仍需要写入 localStorage 以确保事情保持同步。缓存条目每 12 小时过期一次并被删除以保持 localStorage 内存较低。如果您的应用要长时间打开,这可以在很长的setInterval 上完成。

    import { HttpRequest, HttpResponse } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    
    const LOCAL_CACHE_KEY = 'localCache';
    const STALE_TIME = 1000 * 60 * 60 * 12; // 12 hours
    
    @Injectable()
    export class CacheService {
        cacheMap: Map<string, any>;
    
        constructor() {
            const map = localStorage.getItem(LOCAL_CACHE_KEY);
            if (map) {
                this.cacheMap = new Map(JSON.parse(map));
            } else {
                this.cacheMap = new Map(null);
            }
            this.cacheMap.forEach((value, key) => {
                if (Date.now() - value.addedTime > STALE_TIME) {
                    this.cacheMap.delete(key);
                }
            });
            this.saveCacheMap();
        }
    
        get(req: HttpRequest<any>): HttpResponse<any> | undefined {
            const cached = this.cacheMap.get(req.urlWithParams);
    
            if (!cached || Date.now() - cached.addedTime > STALE_TIME) {
                return undefined;
            }
            return cached.data;
        }
    
        set(req: HttpRequest<any>, response: HttpResponse<any>): void {
            if (response.status === 200) {
                const entry = { data: response.body, addedTime: Date.now() };
                this.cacheMap.set(req.urlWithParams, entry);
                this.saveCacheMap();
            }
        }
    
        private saveCacheMap(): void {
            localStorage.setItem(LOCAL_CACHE_KEY, JSON.stringify([...this.cacheMap]));
        }
    }
    

    链接答案的唯一另一个重大变化是我没有将整个响应存储在地图中,而只是body。从 localStorage 加载时,response.clone() 方法丢失了,因此存储整个响应没有意义,这让我们节省了一点存储空间。不过,这确实需要在 CacheInterceptor 中创建一个新的 HttpResponse。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-27
      • 1970-01-01
      相关资源
      最近更新 更多