最近是在使用 LoopBack@^2.19.1 做一个页面,加载图片,发现每次都是请求新的图片 响应头:Cache-Control: public, max-age=0
,于是查看文档研究了一下。
LooBack 文档对于这部分的介绍少得可怜。
疑问
1、首先看官方文档的介绍。 官方文档:loopback.io/doc/en/lb2/...
json
// server/middleware.json
// 关键配置
// client 目录就是静态资源目录
{
"files": {
"loopback#static": {
"params": "$!../client"
}
}
}
如果想设置资源的可以被缓存的最长时间(即设置响应头:Cache-Control: max-age=3600
),文档没有说明,该怎么办呢?
我是用了笨办法:看源码。
2、首先理清楚依赖:loopback@^2.19.1 -> express@^4.16.2
json
// node_modules\loopback\package.json
{
"name": "loopback",
"version": "2.42.0",
"engines": {
"node": ">=4.0.0"
},
"dependencies": {
"express": "^4.16.2"
}
}
3、看 Loopback 源码的入口和项目目录结构。
重点看 index.js
和 server/middleware/static.js
vbnet
│ index.js
│ package.json
│
├─common
│ └─models
│ access-token.js
│ access-token.json
│ acl.js
│ acl.json
│ application.js
│ application.json
│ change.js
│ change.json
│ checkpoint.js
│ checkpoint.json
│ email.js
│ email.json
│ key-value-model.js
│ key-value-model.json
│ README.md
│ role-mapping.js
│ role-mapping.json
│ role.js
│ role.json
│ scope.js
│ scope.json
│ user.js
│ user.json
├─lib
│ │ access-context.js
│ │ application.js
│ │ browser-express.js
│ │ builtin-models.js
│ │ current-context.js
│ │ express-middleware.js
│ │ loopback.js
│ │ model.js
│ │ persisted-model.js
│ │ registry.js
│ │ runtime.js
│ │ server-app.js
│ │ utils.js
│ │
│ └─connectors
│ base-connector.js
│ mail.js
│ memory.js
│
├─server
│ └─middleware
│ context.js
│ error-handler.js
│ favicon.js
│ rest.js
│ static.js
│ status.js
│ token.js
│ url-not-found.js
│
└─templates
reset-form.ejs
verify.ejs
4、关键字搜索 static
发现 node_modules\loopback\server\middleware\static.js
js
// 省略文件版本信息
/**
* Serve static assets of a LoopBack application.
*
* @param {string} root The root directory from which the static assets are to
* be served.
* @param {object} options Refer to
* [express documentation](http://expressjs.com/4x/api.html#express.static)
* for the full list of available options.
* @header loopback.static(root, [options])
*/
module.exports = require('express').static;
从注释可以看出,第一个参数(root)是静态目录的文件夹。第二个参数是可选的,它是 object,查看 express static 文档:expressjs.com/4x/api.html...

5、尝试设置 1 -> 不生效。
json
// server/middlewar.json
{
"files": {
"loopback#static": {
"params": "$!../client",
"maxAge": "1h"
}
}
}
6、尝试设置 2 -> 不生效。
json
// server/middlewar.json
{
"files": {
"loopback#static": {
"params": "$!../client",
"options": {"maxAge": "1h"}
}
}
}
7、尝试设置 3 -> 生效。
json
// server/middlewar.json
{
"files": {
"loopback#static": {
"params": ["$!../client", {"maxAge": "1h"}]
}
}
}
继续深入探索
别问我为什么要尝试,因为那时还没看懂 LoopBack 2 是怎样初始化中间件的。 今天看懂了。
1、开启 debug
bash
DEBUG=loopback node .
参考文档:loopback.io/doc/en/lb2/...
2、看日志
swift
loopback:boot:executor Configuring middleware "xxx\\node_modules\\loopback"#static
3、打开项目 loopback-boot 项目搜索 loopback:boot:executor
arduino
// node_modules\loopback-boot\lib\executor.js
4、搜索 Configuring middleware
ini
function setupMiddleware(app, instructions) {
if (!instructions.middleware) {
// the browserified client does not support middleware
return;
}
// Phases can be empty
var phases = instructions.middleware.phases || [];
assert(Array.isArray(phases),
g.f('{{instructions.middleware.phases}} must be an {{array}}'));
var middleware = instructions.middleware.middleware;
assert(Array.isArray(middleware),
'instructions.middleware.middleware must be an object');
debug('Defining middleware phases %j', phases);
app.defineMiddlewarePhases(phases);
middleware.forEach(function(data) {
debug('Configuring middleware %j%s', data.sourceFile,
data.fragment ? ('#' + data.fragment) : '');
var factory = requireNodeOrEsModule(data.sourceFile);
if (data.fragment) {
factory = factory[data.fragment].bind(factory);
}
assert(typeof factory === 'function',
'Middleware factory must be a function');
var opts = {
useEnvVars: true,
};
data.config = getUpdatedConfigObject(app, data.config, opts);
app.middlewareFromConfig(factory, data.config);
});
}
5、打开项目 loopback
搜索 middlewareFromConfig
js
node_modules\loopback\lib\server-app.js
/**
* Register a middleware using a factory function and a JSON config.
*
* **Example**
*
* ```js
* app.middlewareFromConfig(compression, {
* enabled: true,
* phase: 'initial',
* params: {
* threshold: 128
* }
* });
* ```
*
* @param {function} factory The factory function creating a middleware handler.
* Typically a result of `require()` call, e.g. `require('compression')`.
* @options {Object} config The configuration.
* @property {String} phase The phase to register the middleware in.
* @property {Boolean} [enabled] Whether the middleware is enabled.
* Default: `true`.
* @property {Array|*} [params] The arguments to pass to the factory
* function. Either an array of arguments,
* or the value of the first argument when the factory expects
* a single argument only.
* @property {Array|string|RegExp} [paths] Optional list of paths limiting
* the scope of the middleware.
*
* @returns {object} this (fluent API)
*
* @header app.middlewareFromConfig(factory, config)
*/
proto.middlewareFromConfig = function(factory, config) {
assert(typeof factory === 'function', '"factory" must be a function');
assert(typeof config === 'object', '"config" must be an object');
assert(typeof config.phase === 'string' && config.phase,
'"config.phase" must be a non-empty string');
if (config.enabled === false)
return;
var params = config.params;
if (params === undefined) {
params = [];
} else if (!Array.isArray(params)) {
params = [params];
}
var handler = factory.apply(null, params);
// 省略了一些代码
return this;
};
6、然后看到
js
var params = config.params;
if (params === undefined) {
params = [];
} else if (!Array.isArray(params)) {
params = [params];
}
var handler = factory.apply(null, params);
然后就可以解释为什么要配置成
json
// server/middlewar.json
{
"files": {
"loopback#static": {
"params": ["$!../client", {"maxAge": "30d"}]
}
}
}