APICACHE : Express/Node的API响应缓存中间件

APICACHE : Express/Node的API响应缓存中间件

翻译来源:https://gitee.com/yunwisdoms/apicache

支持Redis或具有自动清除功能的内置内存引擎。

为啥造这个轮子?

因为简单数据/响应的路由缓存也应该很简单。

 

用法

要使用,只需将中间件(例如:)注入apicache.middleware('5 minutes', [optionalMiddlewareToggle])到您的路由中。其他一切都是不可思议的。

缓存路线

import express from 'express'
import apicache from 'apicache'

let app = express()
let cache = apicache.middleware

app.get('/api/collection/:id?', cache('5 minutes'), (req, res) => {
  // do some work... this will only occur once per 5 minutes
  res.json({ foo: 'bar' })
})

缓存所有路线

let cache = apicache.middleware

app.use(cache('5 minutes'))

app.get('/will-be-cached', (req, res) => {
  res.json({ success: true })
})

与Redis一起使用

import express from 'express'
import apicache from 'apicache'
import redis from 'redis'

let app = express()

// if redisClient option is defined, apicache will use redis client
// instead of built-in memory store
let cacheWithRedis = apicache.options({ redisClient: redis.createClient() }).middleware

app.get('/will-be-cached', cacheWithRedis('5 minutes'), (req, res) => {
  res.json({ success: true })
})

缓存分组和手动控制

import apicache from 'apicache'
let cache = apicache.middleware

app.use(cache('5 minutes'))

// routes are automatically added to index, but may be further added
// to groups for quick deleting of collections
app.get('/api/:collection/:item?', (req, res) => {
  req.apicacheGroup = req.params.collection
  res.json({ success: true })
})

// add route to display cache performance (courtesy of @killdash9)
app.get('/api/cache/performance', (req, res) => {
  res.json(apicache.getPerformance())
})

// add route to display cache index
app.get('/api/cache/index', (req, res) => {
  res.json(apicache.getIndex())
})

// add route to manually clear target/group
app.get('/api/cache/clear/:target?', (req, res) => {
  res.json(apicache.clear(req.params.target))
})

/*

GET /api/foo/bar --> caches entry at /api/foo/bar and adds a group called 'foo' to index
GET /api/cache/index --> displays index
GET /api/cache/clear/foo --> clears all cached entries for 'foo' group/collection

*/

与中间件切换配合使用以进行精细控制

// higher-order function returns false for responses of other status codes (e.g. 403, 404, 500, etc)
const onlyStatus200 = (req, res) => res.statusCode === 200

const cacheSuccesses = cache('5 minutes', onlyStatus200)

app.get('/api/missing', cacheSuccesses, (req, res) => {
  res.status(404).json({ results: 'will not be cached' })
})

app.get('/api/found', cacheSuccesses, (req, res) => {
  res.json({ results: 'will be cached' })
})

防止将高速缓存控制标头“ max-age”自动设置为有效期限

let cache = apicache.options({
  headers: {
    'cache-control': 'no-cache',
  },
}).middleware

let cache5min = cache('5 min') // continue to use normally

 

API

  • apicache.options([globalOptions])-全局选项的获取器/设置器。如果用作设置器,此函数是可链接的,允许您执行诸如...说...返回中间件之类的操作。
  • apicache.middleware([duration], [toggleMiddleware], [localOptions])-您的路线中将使用的实际中间件。duration格式为“ [长度] [单位]”,如"10 minutes"或所示"1 day"。第二个参数是中间件切换功能,接受请求和响应参数,并且必须返回true以启用对请求的缓存。第三个参数是将覆盖全局选项并仅影响此中间件的选项。
  • middleware.options([localOptions]) -用于特定于中间件的选项的获取器/设置器,它将覆盖全局选项。
  • apicache.getPerformance() -返回当前的缓存性能(缓存命中率)
  • apicache.getIndex() -返回[键的当前缓存索引]
  • apicache.clear([target]) -清除缓存目标(键或组),或清除整个缓存(如果未传递任何值),返回新索引。
  • apicache.newInstance([options]) -用于创建新的ApiCache实例(默认情况下,仅要求此库共享一个公共实例)
  • apicache.clone() -用于创建具有与当前实例相同选项的新ApiCache实例

可用选项(默认为第一个值)

{
  debug:            false|true,     // if true, enables console output
  defaultDuration:  '1 hour',       // should be either a number (in ms) or a string, defaults to 1 hour
  enabled:          true|false,     // if false, turns off caching globally (useful on dev)
  redisClient:      client,         // if provided, uses the [node-redis](https://github.com/NodeRedis/node_redis) client instead of [memory-cache](https://github.com/ptarjan/node-cache)
  appendKey:        fn(req, res),   // appendKey takes the req/res objects and returns a custom value to extend the cache key
  headerBlacklist:  [],             // list of headers that should never be cached
  statusCodes: {
    exclude:        [],             // list status codes to specifically exclude (e.g. [404, 403] cache all responses unless they had a 404 or 403 status)
    include:        [],             // list status codes to require (e.g. [200] caches ONLY responses with a success/200 code)
  },
  trackPerformance: false,          // enable/disable performance tracking... WARNING: super cool feature, but may cause memory overhead issues
  headers: {
    // 'cache-control':  'no-cache' // example of header overwrite
  }
}

*可选:打字稿类型(由@danielsogl提供

$ npm install -D @types/apicache

 

自定义缓存键

有时您需要自定义键(例如,按会话或按方法保存路由)。我们变得很容易!

注意:密钥生成中使用的所有req / res属性必须事先设置(上游)。将来的高速缓存命中时将跳过整个路由逻辑块,因此它不能依赖那些参数。

apicache.options({
  appendKey: (req, res) => req.method + res.session.id,
})

 

缓存密钥组

通常,对高速缓存条目进行分组(例如,通过集合(在API中))对我们有利。例如,如果我们更新了“ post”集合中的某些内容,这将使我们能够清除所有缓存的“ post”请求。在req.apicacheGroup = [somevalue];路线中添加简单路线即可启用此功能。请参见下面的示例:

var apicache = require('apicache')
var cache = apicache.middleware

// GET collection/id
app.get('/api/:collection/:id?', cache('1 hour'), function(req, res, next) {
  req.apicacheGroup = req.params.collection
  // do some work
  res.send({ foo: 'bar' })
})

// POST collection/id
app.post('/api/:collection/:id?', function(req, res, next) {
  // update model
  apicache.clear(req.params.collection)
  res.send('added a new item, so the cache has been cleared')
})

此外,您可以使用以下路由将手动缓存控制添加到先前的项目:

// GET apicache index (for the curious)
app.get('/api/cache/index', function(req, res, next) {
  res.send(apicache.getIndex())
})

// GET apicache index (for the curious)
app.get('/api/cache/clear/:key?', function(req, res, next) {
  res.send(200, apicache.clear(req.params.key || req.query.key))
})

 

调试/控制台输出

使用Node环境变量(与广受欢迎的调试模块很好地配合使用)

$ export DEBUG=apicache
$ export DEBUG=apicache,othermoduleThatDebugModuleWillPickUp,etc

通过设置内部选项

import apicache from 'apicache'

apicache.options({ debug: true })

 

客户端旁路

GET在管理站点和公共站点之间共享路由时,您可能希望从公共客户端缓存路由,但从管理客户端缓存不路由。这是通过发送"x-apicache-bypass": true标头以及来自管理员的请求来实现的。此标头标志的存在将绕过缓存,确保您不会查看过时的数据。

 

贡献者

特别感谢所有使用此库并报告问题的人员,尤其要感谢以下有助于增加核心功能的活跃用户!

 

错误修正,调整,文档等。

  • @ Amhri,@ Webcascade,@ conmarap,@ cjfurelid,@ scambier,@ lukechilds,@ Red-Lv,@ gesposito,@ viebel,@ RowanMeara,@ GoingFast,@ luin,@ keithws,@ daveross,@ apascal

 

变更日志

  • v1.5.2-多个修复程序:每次讨论默认情况下都弃用缓冲区和_headers,{trackPerformance:false}(抱歉,semver ...)
  • v1.5.1-添加了{trackPerformance}选项以启用/禁用性能跟踪(感谢@fernandolguevara)
  • v1.5.0-为每个路由的缓存指标公开apicache.getPerformance()(@ killdash9继续提供)
  • v1.4.0-缓存控制标头现在在缓存的响应中自动减少(再次感谢@ killdash9)
  • V1.3.0 - [securityfix] apicache头不再嵌入在缓存的响应时NODE_ENV === '生产'(感谢反馈@萨蒂亚-jugran,@smddzcy,@adamelliotfields)。更新了deps,现在需要Node v6.00 +。
  • v1.2.6 -middlewareToggle()现在可防止在缓存命中+虚假切换时响应块(感谢@apascal)
  • v1.2.5-使用本机Node setHeader()而不是express.js header()(感谢@keithws和@daveross)
  • v1.2.4-使用旧的和新的Buffer创建语法将内容类型强制为Buffer
  • v1.2.3-将etag添加到if-none-match 304支持(感谢测试/问题@svozza)
  • V1.2.2 -修正:ioredis.expire PARAMS(感谢@GoingFast和@luin)
  • v1.2.1-更新了部门
  • V1.2.0 -支持ioredis(感谢@Rauttis)
  • v1.1.1-过期超时清除和压缩下内容标头保存的错误修正(感谢@RowanMeara和@samimakicc)。
  • v1.1.0-添加了自定义的appendKey函数的要求很高的功能(以前仅采用了指向单个请求属性的路径)。现在获取(请求,响应)对象,并返回一些要附加到缓存键的值。
  • V1.0.0 -冲压v0.11.2进入正式生产的版本,现在开始分支版本2.x开发(改版)
  • v0.11.2 -dev-deps更新,由@danielsogl提供
  • v0.11.1-更正了状态代码缓存,并且在不缓存时不再发送max-age标头。middlewareToggle现在可以与statusCode检查示例一起使用(在shouldCacheResponse周期中进行检查)
  • v0.11.0-为defaultDuration选项添加了字符串支持,以前只是数字ms-谢谢@davebaol
  • v0.10.0-通过options.headersBlacklist(感谢@andredigenova)向黑名单头添加了功能(防止缓存)
  • v0.9.1-在v1.x分支的准备工作中添加了eslint,在主分支测试中将次要ES6添加到了ES5
  • v0.9.0-已更正的Node v7.7和v8与restify发生冲突(非常感谢@svozza追逐此问题并在Restify自身中修复上游问题)。添加了工作服。添加了middleware.localOptions支持(感谢@vectart)。增加了通过选项覆盖/嵌入标头(例如“ cache-control”:“ no-cache”)的功能。
  • v0.8.8-更正为使用节点v7 +标头(感谢@peteboere)
  • v0.8.6,v0.8.7-自述文件更新
  • v0.8.5-开发依赖项更新(感谢@danielsogl)
  • v0.8.4-更正了缓冲区的积累,并提供了测试支持(感谢@ubergesundheit)
  • v0.8.3-添加了对x-apicache-bypass和x-apicache-force-fetch(旧版)的测试,并修复了后者中的错误(感谢@ Red-Lv)
  • v0.8.2-测试套件和模拟API重构(感谢@svozza)
  • v0.8.1-修复了resify支持并添加了适当的测试(感谢@svozza)
  • v0.8.0-修改响应累积(感谢@ killdash9)以支持res.write + res.end累积,从而允许与restify集成。添加gzip支持(现在需要Node v4.3.2 +)和测试。
  • v0.7.0-内部设置响应对象的缓存控制/最大年龄标头
  • v0.6.0-删除了最终依赖项(调试)并更新了自述文件
  • v0.5.0-更新内部以使用res.end而不是res.send / res.json / res.jsonp,允许任何响应类型,添加了redis测试
  • v0.4.0-删除了lodash和内存缓存外部依赖项,并将节点版本要求提高到4.0.0+以允许Object.assign本机支持

英文原文

A simple API response caching middleware for Express/Node using plain-english durations.

Supports Redis or built-in memory engine with auto-clearing.

    

Why?

Because route-caching of simple data/responses should ALSO be simple.

Usage

To use, simply inject the middleware (example: apicache.middleware('5 minutes', [optionalMiddlewareToggle])) into your routes. Everything else is automagic.

Cache a route

import express from 'express'
import apicache from 'apicache'

let app = express()
let cache = apicache.middleware

app.get('/api/collection/:id?', cache('5 minutes'), (req, res) => {
  // do some work... this will only occur once per 5 minutes
  res.json({ foo: 'bar' })
})

Cache all routes

let cache = apicache.middleware

app.use(cache('5 minutes'))

app.get('/will-be-cached', (req, res) => {
  res.json({ success: true })
})

Use with Redis

import express from 'express'
import apicache from 'apicache'
import redis from 'redis'

let app = express()

// if redisClient option is defined, apicache will use redis client
// instead of built-in memory store
let cacheWithRedis = apicache.options({ redisClient: redis.createClient() }).middleware

app.get('/will-be-cached', cacheWithRedis('5 minutes'), (req, res) => {
  res.json({ success: true })
})

Cache grouping and manual controls

import apicache from 'apicache'
let cache = apicache.middleware

app.use(cache('5 minutes'))

// routes are automatically added to index, but may be further added
// to groups for quick deleting of collections
app.get('/api/:collection/:item?', (req, res) => {
  req.apicacheGroup = req.params.collection
  res.json({ success: true })
})

// add route to display cache performance (courtesy of @killdash9)
app.get('/api/cache/performance', (req, res) => {
  res.json(apicache.getPerformance())
})

// add route to display cache index
app.get('/api/cache/index', (req, res) => {
  res.json(apicache.getIndex())
})

// add route to manually clear target/group
app.get('/api/cache/clear/:target?', (req, res) => {
  res.json(apicache.clear(req.params.target))
})

/*

GET /api/foo/bar --> caches entry at /api/foo/bar and adds a group called 'foo' to index
GET /api/cache/index --> displays index
GET /api/cache/clear/foo --> clears all cached entries for 'foo' group/collection

*/

Use with middleware toggle for fine control

// higher-order function returns false for responses of other status codes (e.g. 403, 404, 500, etc)
const onlyStatus200 = (req, res) => res.statusCode === 200

const cacheSuccesses = cache('5 minutes', onlyStatus200)

app.get('/api/missing', cacheSuccesses, (req, res) => {
  res.status(404).json({ results: 'will not be cached' })
})

app.get('/api/found', cacheSuccesses, (req, res) => {
  res.json({ results: 'will be cached' })
})

Prevent cache-control header "max-age" from automatically being set to expiration age

let cache = apicache.options({
  headers: {
    'cache-control': 'no-cache',
  },
}).middleware

let cache5min = cache('5 min') // continue to use normally

API

  • apicache.options([globalOptions]) - getter/setter for global options. If used as a setter, this function is chainable, allowing you to do things such as... say... return the middleware.
  • apicache.middleware([duration], [toggleMiddleware], [localOptions]) - the actual middleware that will be used in your routes. duration is in the following format "[length][unit]", as in "10 minutes" or "1 day". A second param is a middleware toggle function, accepting request and response params, and must return truthy to enable cache for the request. Third param is the options that will override global ones and affect this middleware only.
  • middleware.options([localOptions]) - getter/setter for middleware-specific options that will override global ones.
  • apicache.getPerformance() - returns current cache performance (cache hit rate)
  • apicache.getIndex() - returns current cache index [of keys]
  • apicache.clear([target]) - clears cache target (key or group), or entire cache if no value passed, returns new index.
  • apicache.newInstance([options]) - used to create a new ApiCache instance (by default, simply requiring this library shares a common instance)
  • apicache.clone() - used to create a new ApiCache instance with the same options as the current one

Available Options (first value is default)

{
  debug:            false|true,     // if true, enables console output
  defaultDuration:  '1 hour',       // should be either a number (in ms) or a string, defaults to 1 hour
  enabled:          true|false,     // if false, turns off caching globally (useful on dev)
  redisClient:      client,         // if provided, uses the [node-redis](https://github.com/NodeRedis/node_redis) client instead of [memory-cache](https://github.com/ptarjan/node-cache)
  appendKey:        fn(req, res),   // appendKey takes the req/res objects and returns a custom value to extend the cache key
  headerBlacklist:  [],             // list of headers that should never be cached
  statusCodes: {
    exclude:        [],             // list status codes to specifically exclude (e.g. [404, 403] cache all responses unless they had a 404 or 403 status)
    include:        [],             // list status codes to require (e.g. [200] caches ONLY responses with a success/200 code)
  },
  trackPerformance: false,          // enable/disable performance tracking... WARNING: super cool feature, but may cause memory overhead issues
  headers: {
    // 'cache-control':  'no-cache' // example of header overwrite
  }
}

*Optional: Typescript Types (courtesy of @danielsogl)

$ npm install -D @types/apicache

Custom Cache Keys

Sometimes you need custom keys (e.g. save routes per-session, or per method). We've made it easy!

Note: All req/res attributes used in the generation of the key must have been set previously (upstream). The entire route logic block is skipped on future cache hits so it can't rely on those params.

apicache.options({
  appendKey: (req, res) => req.method + res.session.id,
})

Cache Key Groups

Oftentimes it benefits us to group cache entries, for example, by collection (in an API). This would enable us to clear all cached "post" requests if we updated something in the "post" collection for instance. Adding a simple req.apicacheGroup = [somevalue]; to your route enables this. See example below:

var apicache = require('apicache')
var cache = apicache.middleware

// GET collection/id
app.get('/api/:collection/:id?', cache('1 hour'), function(req, res, next) {
  req.apicacheGroup = req.params.collection
  // do some work
  res.send({ foo: 'bar' })
})

// POST collection/id
app.post('/api/:collection/:id?', function(req, res, next) {
  // update model
  apicache.clear(req.params.collection)
  res.send('added a new item, so the cache has been cleared')
})

Additionally, you could add manual cache control to the previous project with routes such as these:

// GET apicache index (for the curious)
app.get('/api/cache/index', function(req, res, next) {
  res.send(apicache.getIndex())
})

// GET apicache index (for the curious)
app.get('/api/cache/clear/:key?', function(req, res, next) {
  res.send(200, apicache.clear(req.params.key || req.query.key))
})

Debugging/Console Out

Using Node environment variables (plays nicely with the hugely popular debug module)

$ export DEBUG=apicache
$ export DEBUG=apicache,othermoduleThatDebugModuleWillPickUp,etc

By setting internal option

import apicache from 'apicache'

apicache.options({ debug: true })

Client-Side Bypass

When sharing GET routes between admin and public sites, you'll likely want the routes to be cached from your public client, but NOT cached when from the admin client. This is achieved by sending a "x-apicache-bypass": true header along with the requst from the admin. The presence of this header flag will bypass the cache, ensuring you aren't looking at stale data.

Contributors

Special thanks to all those that use this library and report issues, but especially to the following active users that have helped add to the core functionality!

  • @killdash9 - restify support, performance/stats system, and too much else at this point to list
  • @svozza - added restify tests, test suite refactor, and fixed header issue with restify. Node v7 + Restify v5 conflict resolution, etag/if-none-match support, etcetc, etc. Triple thanks!!!
  • @andredigenova - Added header blacklist as options, correction to caching checks
  • @peteboere - Node v7 headers update
  • @rutgernation - JSONP support
  • @enricsangra - added x-apicache-force-fetch header
  • @tskillian - custom appendKey path support
  • @agolden - Content-Encoding preservation (for gzip, etc)
  • @davidyang - express 4+ compatibility
  • @nmors - redis support
  • @maytis@ashwinnaidu - redis expiration
  • @ubergesundheit - Corrected buffer accumulation using res.write with Buffers
  • @danielsogl - Keeping dev deps up to date, Typescript Types
  • @vectart - Added middleware local options support
  • @davebaol - Added string support to defaultDuration option (previously just numeric ms)
  • @Rauttis - Added ioredis support
  • @fernandolguevara - Added opt-out for performance tracking, great emergency fix, thank you!!

Bugfixes, tweaks, documentation, etc.

  • @Amhri, @Webcascade, @conmarap, @cjfurelid, @scambier, @lukechilds, @Red-Lv, @gesposito, @viebel, @RowanMeara, @GoingFast, @luin, @keithws, @daveross, @apascal

Changelog

  • v1.5.2 - multiple fixes: Buffer deprecation and _headers deprecation, { trackPerformance: false } by default per discussion (sorry semver...)
  • v1.5.1 - adds { trackPerformance } option to enable/disable performance tracking (thanks @fernandolguevara)
  • v1.5.0 - exposes apicache.getPerformance() for per-route cache metrics (@killdash9 continues to deliver)
  • v1.4.0 - cache-control header now auto-decrements in cached responses (thanks again, @killdash9)
  • v1.3.0 - [securityfix] apicache headers no longer embedded in cached responses when NODE_ENV === 'production' (thanks for feedback @satya-jugran, @smddzcy, @adamelliotfields). Updated deps, now requiring Node v6.00+.
  • v1.2.6 - middlewareToggle() now prevents response block on cache hit + falsy toggle (thanks @apascal)
  • v1.2.5 - uses native Node setHeader() rather than express.js header() (thanks @keithws and @daveross)
  • v1.2.4 - force content type to Buffer, using old and new Buffer creation syntax
  • v1.2.3 - add etag to if-none-match 304 support (thanks for the test/issue @svozza)
  • v1.2.2 - bugfix: ioredis.expire params (thanks @GoingFast and @luin)
  • v1.2.1 - Updated deps
  • v1.2.0 - Supports ioredis (thanks @Rauttis)
  • v1.1.1 - bugfixes in expiration timeout clearing and content header preservation under compression (thanks @RowanMeara and @samimakicc).
  • v1.1.0 - added the much-requested feature of a custom appendKey function (previously only took a path to a single request attribute). Now takes (request, response) objects and returns some value to be appended to the cache key.
  • v1.0.0 - stamping v0.11.2 into official production version, will now begin developing on branch v2.x (redesign)
  • v0.11.2 - dev-deps update, courtesy of @danielsogl
  • v0.11.1 - correction to status code caching, and max-age headers are no longer sent when not cached. middlewareToggle now works as intended with example of statusCode checking (checks during shouldCacheResponse cycle)
  • v0.11.0 - Added string support to defaultDuration option, previously just numeric ms - thanks @davebaol
  • v0.10.0 - added ability to blacklist headers (prevents caching) via options.headersBlacklist (thanks @andredigenova)
  • v0.9.1 - added eslint in prep for v1.x branch, minor ES6 to ES5 in master branch tests
  • v0.9.0 - corrected Node v7.7 & v8 conflicts with restify (huge thanks to @svozza for chasing this down and fixing upstream in restify itself). Added coveralls. Added middleware.localOptions support (thanks @vectart). Added ability to overwrite/embed headers (e.g. "cache-control": "no-cache") through options.
  • v0.8.8 - corrected to use node v7+ headers (thanks @peteboere)
  • v0.8.6, v0.8.7 - README update
  • v0.8.5 - dev dependencies update (thanks @danielsogl)
  • v0.8.4 - corrected buffer accumulation, with test support (thanks @ubergesundheit)
  • v0.8.3 - added tests for x-apicache-bypass and x-apicache-force-fetch (legacy) and fixed a bug in the latter (thanks @Red-Lv)
  • v0.8.2 - test suite and mock API refactor (thanks @svozza)
  • v0.8.1 - fixed restify support and added appropriate tests (thanks @svozza)
  • v0.8.0 - modifies response accumulation (thanks @killdash9) to support res.write + res.end accumulation, allowing integration with restify. Adds gzip support (Node v4.3.2+ now required) and tests.
  • v0.7.0 - internally sets cache-control/max-age headers of response object
  • v0.6.0 - removed final dependency (debug) and updated README
  • v0.5.0 - updated internals to use res.end instead of res.send/res.json/res.jsonp, allowing for any response type, adds redis tests
  • v0.4.0 - dropped lodash and memory-cache external dependencies, and bumped node version requirements to 4.0.0+ to allow Object.assign native support
发布了153 篇原创文章 · 获赞 803 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/Aria_Miazzy/article/details/103903232