Skip to content

Commit

Permalink
chore: fix http
Browse files Browse the repository at this point in the history
  • Loading branch information
cipchk committed Nov 29, 2023
1 parent f0ebea1 commit a2a6f0a
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 82 deletions.
22 changes: 22 additions & 0 deletions packages/cache/docs/interceptor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
order: 3
title: Interceptor
type: Documents
---

# 写在前面

搭配 `httpCacheInterceptor` Http 拦截器,可以将缓存应用到 Http 请求当中。它只有几个特征:

- 支持缓存过期时间
- 支持自定义缓存 KEY
- 支持任何 Http 请求、任何数据格式
- 符合 Http 缓存响应标准 `Cache-Control`

# 如何使用

`withInterceptors` 中引入 `httpCacheInterceptor`

```ts
provideHttpClient(withInterceptors([httpCacheInterceptor]))
```
1 change: 0 additions & 1 deletion packages/cache/public_api.ts
Original file line number Diff line number Diff line change
@@ -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';
105 changes: 47 additions & 58 deletions packages/cache/src/cache.interceptor.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<any>, HttpEvent<any>> = map(ev => save(srv, ev, options));
if (options.enabled === false) {
return next(req).pipe(mapPipe);
}

private save(ev: HttpEvent<any>, options: CacheOptions): HttpEvent<any> {
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<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const options: CacheOptions = {
enabled: true,
emitNotify: true,
saveType: 'm',
...this.cog,
...req.context.get(CACHE)
};
const mapPipe: OperatorFunction<HttpEvent<any>, HttpEvent<any>> = 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<HttpEvent<any>>(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<HttpEvent<any>>(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<any>, options: CacheOptions): HttpEvent<any> {
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;
}
4 changes: 0 additions & 4 deletions packages/cache/src/cache.module.ts

This file was deleted.

31 changes: 14 additions & 17 deletions packages/cache/src/cache.service.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* 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';

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' })
Expand All @@ -22,21 +22,18 @@ export class CacheService implements OnDestroy {
private meta: Set<string> = new Set<string>();
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();
}
Expand Down
3 changes: 1 addition & 2 deletions packages/cache/src/cache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -42,7 +41,7 @@ describe('cache: service', () => {
providers.push(provideAlainConfig({ cache: options }));
}
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, DelonCacheModule],
imports: [HttpClientTestingModule],
providers
});

Expand Down

0 comments on commit a2a6f0a

Please sign in to comment.