Чувствительность к регистру статических активов Node.js Express
Как я могу установить, будет ли маршрутизация для express.static
чувствителен к регистру? например, должен ли Express обрабатывать запрос image.jpeg
обслуживая локальный файл с именем Image.jpeg
,
При звонке есть опция caseSensitive express.Router([options])
(как определено на http://expressjs.com/en/4x/api.html), но это не вариант при вызове express.static(root, [options])
(документация по той же ссылке).
По умолчанию я получаю различное поведение при обработке статических файлов от томов без учета регистра (/Mac OS X) до томов с учетом регистра (/Linux). Это приводит к противоречивым ошибкам в нашем приложении - где что-то с несоответствием регистра работает локально в Mac OS X, но терпит неудачу при развертывании на сервер Linux.
2 ответа
Мне также дали несколько советов для простого альтернативного решения:
Установите политику, что все ваши статические файлы и пути должны быть в нижнем регистре.
Примените политику к файлам, добавив шаг сборки, который проверяет их.
Примените политику к запросам, добавив некоторое промежуточное программное обеспечение, которое отклоняет запросы к статической папке, если в пути обнаружены символы верхнего регистра.
Я тоже этого хотел, поэтому быстро выбрал 404 запроса с несовпадающим регистром.
Это не особенно эффективно, поэтому я запускаю его только в разработке. Он только проверяет имя файла. Он не проверяет регистр папок над файлом.
Как это использовать:
var express = require('express');
var app = express();
// You can do this before or after the express() call
// But it must come before express.static() is called
var inDevelopment = (process.NODE_ENV || 'local') === 'local';
if (inDevelopment) {
require('./modules/makeExpressStaticCaseSensitive')(express);
}
app.use(express.static(path.join(__dirname, 'public_html')));
Сценарий module/makeExpressStaticCaseSensitive.js
module.exports = function (express) {
var fs = require('fs')
var pathlib = require('path');
var parseUrl = require('express/node_modules/parseurl')
var oldStatic = express.static;
var newStatic = function (root, options) {
var opts = Object.create(options || null);
var originalHandler = oldStatic(root, options);
var wrappedHandler = function (req, res, next) {
var filepath = pathlib.join(root, parseUrl(req).pathname);
var dirpath = pathlib.dirname(filepath);
var filename = pathlib.basename(filepath);
// @todo Reading the entire directory listing and then searching it is quite inefficient for large folders
// We should find a more efficient way to do this for one file at a time
fs.readdir(dirpath, function (err, files) {
if (err) return next(err);
var fileIsThere = files.indexOf(filename) >= 0;
if (fileIsThere) {
originalHandler(req, res, next);
} else {
res.status(404).end();
}
});
};
return wrappedHandler;
};
express.static = newStatic;
};
Я написал более эффективную версию, которая кэширует вывод readdir()
на несколько секунд, и проверьте весь путь, но он несколько длиннее.
Недавно я столкнулся с этой проблемой и отладил базовый код для express.static. Я не мог найти решения для этого сценария, кроме как сделать именование последовательным. Но я подумал, что будет хорошей идеей поделиться тем, что происходит под капотами. Нет возможности настроить поведение, и чувствительность к регистру определяется файловой системой хоста (MacOS APFS, которая по умолчанию чувствительна к регистру для меня).
express.static
фактически использует пакет npm serve-static
а также serve-static
использует пакет npm send
чтобы вернуть файл.
И send фактически использует Node.js fs.stat
чтобы проверить, существует ли файл перед возвратом файла. И чувствительность к регистру этого вызова зависит от файловой системы хоста Express.js. Для файловых систем хоста, чувствительных к регистру, вызов вернется, а файл не найден.
Это отрывок кода, вызывающего проблему.
fs.stat(path, function onstat (err, stat) {
if (err && err.code === 'ENOENT' && !extname(path) && path[path.length - 1] !== sep) {
// not found, check extensions
return next(err)
}
if (err) return self.onStatError(err)
if (stat.isDirectory()) return self.redirect(path)
self.emit('file', path, stat)
self.send(path, stat)
})
Я не нахожу никаких параметров, которые можно передать ему из Express, которые позволяют настраивать поведение. express.static также выполняет множество важных функций, таких как установка заголовков ответов (кеширование и т. д.) для файла, поэтому удаление его без эквивалентной замены также не является хорошим решением.