From a2a6f0a295e1c866e80e43736bec541b01694627 Mon Sep 17 00:00:00 2001 From: cipchk Date: Wed, 29 Nov 2023 22:55:55 +0800 Subject: [PATCH] chore: fix http --- packages/cache/docs/interceptor.md | 22 +++++ packages/cache/public_api.ts | 1 - packages/cache/src/cache.interceptor.ts | 105 +++++++++++------------- packages/cache/src/cache.module.ts | 4 - packages/cache/src/cache.service.ts | 31 ++++--- packages/cache/src/cache.spec.ts | 3 +- 6 files changed, 84 insertions(+), 82 deletions(-) create mode 100644 packages/cache/docs/interceptor.md delete mode 100644 packages/cache/src/cache.module.ts diff --git a/packages/cache/docs/interceptor.md b/packages/cache/docs/interceptor.md new file mode 100644 index 0000000000..d6772e041c --- /dev/null +++ b/packages/cache/docs/interceptor.md @@ -0,0 +1,22 @@ +--- +order: 3 +title: Interceptor +type: Documents +--- + +# 写在前面 + +搭配 `httpCacheInterceptor` Http 拦截器,可以将缓存应用到 Http 请求当中。它只有几个特征: + +- 支持缓存过期时间 +- 支持自定义缓存 KEY +- 支持任何 Http 请求、任何数据格式 +- 符合 Http 缓存响应标准 `Cache-Control` + +# 如何使用 + +在 `withInterceptors` 中引入 `httpCacheInterceptor`: + +```ts +provideHttpClient(withInterceptors([httpCacheInterceptor])) +``` diff --git a/packages/cache/public_api.ts b/packages/cache/public_api.ts index bfd4079a7a..2aea6c3eb0 100644 --- a/packages/cache/public_api.ts +++ b/packages/cache/public_api.ts @@ -1,5 +1,4 @@ export * from './src/interface'; export * from './src/cache.service'; -export * from './src/cache.module'; export * from './src/cache.interceptor'; export * from './src/token'; diff --git a/packages/cache/src/cache.interceptor.ts b/packages/cache/src/cache.interceptor.ts index 601f33cc81..61c1e41231 100644 --- a/packages/cache/src/cache.interceptor.ts +++ b/packages/cache/src/cache.interceptor.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponseBase } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { map, Observable, of, OperatorFunction } from 'rxjs'; +import { HttpEvent, HttpInterceptorFn, HttpResponseBase } from '@angular/common/http'; +import { inject } from '@angular/core'; +import { map, of, OperatorFunction } from 'rxjs'; -import { AlainCacheInterceptor, AlainConfigService } from '@delon/util/config'; +import { AlainConfigService } from '@delon/util/config'; import { CacheService } from './cache.service'; import { CacheOptions, CACHE } from './token'; @@ -14,67 +14,56 @@ import { CacheOptions, CACHE } from './token'; * 缓存拦截器 * * @example - * providers: [ - * { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true }, - * ] + * provideHttpClient(withInterceptors([httpCacheInterceptor])), */ -@Injectable() -export class CacheInterceptor implements HttpInterceptor { - private cog?: AlainCacheInterceptor; - constructor( - cogSrv: AlainConfigService, - private srv: CacheService - ) { - this.cog = cogSrv.merge('cache', {})!.interceptor; +export const httpCacheInterceptor: HttpInterceptorFn = (req, next) => { + const cog = inject(AlainConfigService).merge('cache', {})!.interceptor; + const options: CacheOptions = { + enabled: true, + emitNotify: true, + saveType: 'm', + ...cog, + ...req.context.get(CACHE) + }; + const srv = inject(CacheService); + const mapPipe: OperatorFunction, HttpEvent> = map(ev => save(srv, ev, options)); + if (options.enabled === false) { + return next(req).pipe(mapPipe); } - private save(ev: HttpEvent, options: CacheOptions): HttpEvent { - if (!(ev instanceof HttpResponseBase) || !(ev.status >= 200 && ev.status < 300)) return ev; - let expire = options.expire; - if (expire == null) { - const ageMatch = /max-age=(\d+)/g.exec(ev.headers.get('cache-control')?.toLowerCase() ?? ''); - if (ageMatch == null) return ev; - expire = +ageMatch[1]; - } - if (expire > 0) { - this.srv.set(options.key!!, ev, { - type: options.saveType!!, - expire: expire - }); - } - return ev; + if (options.key == null) { + options.key = req.urlWithParams; } - intercept(req: HttpRequest, next: HttpHandler): Observable> { - const options: CacheOptions = { - enabled: true, - emitNotify: true, - saveType: 'm', - ...this.cog, - ...req.context.get(CACHE) - }; - const mapPipe: OperatorFunction, HttpEvent> = map(ev => this.save(ev, options)); - if (options.enabled === false) { - return next.handle(req).pipe(mapPipe); - } - - if (options.key == null) { - options.key = req.urlWithParams; + const cacheData = srv.getNone>(options.key); + if (cacheData != null) { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + console.log( + `%c👽${req.method}->${req.urlWithParams}->from cache(onle in development)`, + 'background:#000;color:#1890ff', + req, + cacheData + ); } + return of(cacheData); + } - const cacheData = this.srv.getNone>(options.key); - if (cacheData != null) { - if (typeof ngDevMode === 'undefined' || ngDevMode) { - console.log( - `%c👽${req.method}->${req.urlWithParams}->from cache(onle in development)`, - 'background:#000;color:#1890ff', - req, - cacheData - ); - } - return of(cacheData); - } + return next(req).pipe(mapPipe); +}; - return next.handle(req).pipe(mapPipe); +function save(srv: CacheService, ev: HttpEvent, options: CacheOptions): HttpEvent { + if (!(ev instanceof HttpResponseBase) || !(ev.status >= 200 && ev.status < 300)) return ev; + let expire = options.expire; + if (expire == null) { + const ageMatch = /max-age=(\d+)/g.exec(ev.headers.get('cache-control')?.toLowerCase() ?? ''); + if (ageMatch == null) return ev; + expire = +ageMatch[1]; + } + if (expire > 0) { + srv.set(options.key!!, ev, { + type: options.saveType!!, + expire: expire + }); } + return ev; } diff --git a/packages/cache/src/cache.module.ts b/packages/cache/src/cache.module.ts deleted file mode 100644 index 773151b668..0000000000 --- a/packages/cache/src/cache.module.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { NgModule } from '@angular/core'; - -@NgModule({}) -export class DelonCacheModule {} diff --git a/packages/cache/src/cache.service.ts b/packages/cache/src/cache.service.ts index b4d1e218b4..7040eaaf56 100644 --- a/packages/cache/src/cache.service.ts +++ b/packages/cache/src/cache.service.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Platform } from '@angular/cdk/platform'; import { HttpClient } from '@angular/common/http'; -import { Inject, Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { BehaviorSubject, Observable, of, map, tap } from 'rxjs'; import { addSeconds } from 'date-fns'; @@ -9,7 +9,7 @@ import { addSeconds } from 'date-fns'; import { AlainCacheConfig, AlainConfigService } from '@delon/util/config'; import { deepGet } from '@delon/util/other'; -import { CacheNotifyResult, CacheNotifyType, ICache, ICacheStore } from './interface'; +import { CacheNotifyResult, CacheNotifyType, ICache } from './interface'; import { DC_STORE_STORAGE_TOKEN } from './local-storage-cache.service'; @Injectable({ providedIn: 'root' }) @@ -22,21 +22,18 @@ export class CacheService implements OnDestroy { private meta: Set = new Set(); private freqTick = 3000; private freqTime: any; - private cog: AlainCacheConfig; - - constructor( - cogSrv: AlainConfigService, - @Inject(DC_STORE_STORAGE_TOKEN) private store: ICacheStore, - private http: HttpClient, - private platform: Platform - ) { - this.cog = cogSrv.merge('cache', { - mode: 'promise', - reName: '', - prefix: '', - meta_key: '__cache_meta' - })!; - if (!platform.isBrowser) return; + private cog: AlainCacheConfig = inject(AlainConfigService).merge('cache', { + mode: 'promise', + reName: '', + prefix: '', + meta_key: '__cache_meta' + })!; + private readonly store = inject(DC_STORE_STORAGE_TOKEN); + private readonly http = inject(HttpClient); + private readonly platform = inject(Platform); + + constructor() { + if (!this.platform.isBrowser) return; this.loadMeta(); this.startExpireNotify(); } diff --git a/packages/cache/src/cache.spec.ts b/packages/cache/src/cache.spec.ts index fe6d9bd087..33d169314a 100644 --- a/packages/cache/src/cache.spec.ts +++ b/packages/cache/src/cache.spec.ts @@ -7,7 +7,6 @@ import { firstValueFrom, Observable, of, filter } from 'rxjs'; import { AlainCacheConfig, provideAlainConfig } from '@delon/util/config'; -import { DelonCacheModule } from './cache.module'; import { CacheService } from './cache.service'; import { ICache } from './interface'; @@ -42,7 +41,7 @@ describe('cache: service', () => { providers.push(provideAlainConfig({ cache: options })); } TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, DelonCacheModule], + imports: [HttpClientTestingModule], providers });