http-svc
Version:
A HTTP request service for browser and node.js
65 lines (61 loc) • 2.07 kB
text/typescript
import { HttpSvcMiddleware } from '@http-svc/middleware'
import { IMiddlewareHandler } from 'types/exports'
import { ICachePayload, ICacheStore, IKeyDict } from 'types/middlewares/cache'
import { getBuiltInMiddlewareName } from '../shared'
/**
* 缓存中间件
* @param ctx
* @param next
* @returns
*/
// 通过dict 获取string key
const getKeyByKeyDict = (url: string, dict: IKeyDict): string => {
return Object.keys(dict)
.sort()
.reduce((k, c) => {
k += `_${c}:${dict[c]}`
return k
}, `api:${url}`)
}
// 缓存handler
export const cache: IMiddlewareHandler<ICachePayload> = async (ctx, next, config) => {
// 兜底
if (!ctx.request || ctx.request.method !== 'GET') return await next()
// 如果没有载荷直接跳过
if (!config?.payload) return await next()
const { store, extra } = config.payload
let key = config.payload.key
// 如果没有缓存的key以及store,则缺少缓存能力的必要条件
if (!key || !store) return await next()
// 如果是字典,则将字典的值排序后得到一个字符串key
if (typeof key !== 'string') key = getKeyByKeyDict(ctx.request.url, key)
// 获取缓存数据,extra由调用方传入,自行与传入的store进行交互(如过期逻辑等)
const data = store.get(key, extra)
// 如果为非空数据,则直接使用这个数据生成response
if (data) {
ctx.response = new Response(JSON.stringify(data), {
status: 200,
statusText: `Cache:${key}`
})
await next()
// 如果本轮读的是缓存,那就无需二次存储了
} else {
await next()
// 如果本轮没有走缓存,那么可以执行设置缓存,设置逻辑由调用方自行控制
if (ctx.response?.data) {
store.set(key, ctx.response.data, extra)
}
}
}
export class HttpSvcCache extends HttpSvcMiddleware<ICachePayload> {
static handler = cache
name = getBuiltInMiddlewareName('CACHE')
constructor(store: ICacheStore) {
super({
handler: cache,
payload: {
store
}
})
}
}