45

Node.jsとExpressを使用しており、次のルーティングがあります。

app.get('/', function(req,res){
    locals.date = new Date().toLocaleDateString();

    res.render('home.ejs', locals);
});

function lessonsRouter (req, res, next)
{
    var lesson = req.params.lesson;
    res.render('lessons/' + lesson + '.ejs', locals_lessons);
}

app.get('/lessons/:lesson*', lessonsRouter);


function viewsRouter (req, res, next)
{
    var controllerName = req.params.controllerName;
    res.render(controllerName + '.ejs', locals_lessons);
}
app.get('/:controllerName', viewsRouter);

レッスンページにDisqusウィジェットがあり、移動すると2つの異なるページが表示されるという奇妙な動作に気づきました(そのmyapp.com/lessonsうちmyapp.com/lessons/の1つには、以前にDisqusで追加したコメントがあり、もう1つにはコメントがありません)。

すべてのURLを末尾のスラッシュなしで「正規化」する方法はありますか?strict routing表現するフラグを追加しようとしましたが、結果は同じでした

ありがとう

4

8 に答える 8

96

TolgaAkyüzによる答えは刺激的ですが、スラッシュの後に文字がある場合は機能しません。たとえば、の代わりににhttp://example.com/api/?q=aリダイレクトされます。http://example.com/apihttp://example.com/api?q=a

これは、リダイレクト先のURLの最後に元のクエリを追加することで問題を修正する、提案されたミドルウェアの改良版です。このバージョンには、アップデートノートに記載されているいくつかの安全機能もあります。

app.use((req, res, next) => {
  if (req.path.substr(-1) === '/' && req.path.length > 1) {
    const query = req.url.slice(req.path.length)
    const safepath = req.path.slice(0, -1).replace(/\/+/g, '/')
    res.redirect(301, safepath + query)
  } else {
    next()
  }
})

2016年の更新:jameskが指摘し、 RFC 1738に記載されているように、末尾のスラッシュは、ドメインの後に何もない場合にのみ省略できます。したがって、http://example.com?q=aは無効なURLでhttp://example.com/?q=aあり、は有効なURLです。このような場合、リダイレクトは実行しないでください。幸いなことに、式req.path.length > 1がそれを処理します。たとえば、URLが与えられるhttp://example.com/?q=aと、パスreq.pathはに等しく/なるため、リダイレクトは回避されます。

アップデート2021:Mattが発見したように、パスの先頭に2つのスラッシュ//があると、危険なリダイレクトが発生します。たとえば、URLは、被害者のブラウザとして、または被害者のブラウザによって解釈されるhttp://example.com//evil.com/リダイレクトを作成します。また、Van Quyetが指摘しているように、適切に処理する必要のある末尾のスラッシュが複数存在する可能性があります。これらの発見により、結果として生じるすべてのスラッシュを単一のスラッシュに置き換えることによってパスを保護する行を追加しました。正規表現リテラルは1回しかコンパイルされないため、セーフガードによって引き起こされるパフォーマンスのオーバーヘッドはごくわずかであると私は信じています。さらに、コード構文がES6に更新されました。//evil.comhttp://evil.comhttps://evil.com/

于 2013-04-02T20:33:37.370 に答える
57

そのためのミドルウェアを追加してみてください。

app.use((req, res, next) => {
  const test = /\?[^]*\//.test(req.url);
  if (req.url.substr(-1) === '/' && req.url.length > 1 && !test)
    res.redirect(301, req.url.slice(0, -1));
  else
    next();
});
于 2012-11-18T18:47:50.290 に答える
28

connect-slashesミドルウェアは、このニーズのために特別に設計されました: https ://npmjs.org/package/connect-slashes

次のコマンドでインストールします。

$ npm install connect-slashes

完全なドキュメントを読む: https ://github.com/avinoamr/connect-slashes

于 2013-02-10T21:24:36.727 に答える
11

他のソリューションで問題が多すぎたため、この回答を追加します。

/**
 * @param {express.Request} req
 * @param {express.Response} res
 * @param {express.NextFunction} next
 * @return {void}
 */
function checkTrailingSlash(req, res, next) {
  const trailingSlashUrl = req.baseUrl + req.url;
  if (req.originalUrl !== trailingSlashUrl) {
    res.redirect(301, trailingSlashUrl);
  } else {
    next();
  }
}

router.use(checkTrailingSlash);

これは翻訳されます:

/page ==> /page/
/page?query=value ==> /page/?query=value
于 2016-03-10T20:51:49.307 に答える
6

一発ギャグ:

router.get('\\S+\/$', function (req, res) {
  return res.redirect(301, req.path.slice(0, -1) + req.url.slice(req.path.length));
});

これは、リダイレクトする必要のあるURLのみをキャッチし、他のURLは無視します。

結果の例:

/         ==> /
/a        ==> /a
/a/       ==> /a
/a/b      ==> /a/b
/a/b/     ==> /a/b
/a/b/?c=d ==> /a/b?c=d
于 2017-05-13T00:49:48.450 に答える
0

上記の答えは多くの場合に機能しますが、GET変数で問題が発生する可能性があり、それを別のエクスプレスミドルウェア内に置くreq.pathと、依存が問題を引き起こし、依存req.urlが望ましくない副作用を引き起こす可能性があります。より厳密な解決策を探している場合は、これでうまくいきます。

// Redirect non trailing slash to trailing slash
app.use(function(req, res, next){
    // Find the query string
    var qsi = req.originalUrl.indexOf('?');
    // Get the path
    var path = req.originalUrl;
    if(qsi > -1) path = path.substr(0, qsi);
    // Continue if the path is good or it's a static resource
    if(path.substr(-1) === '/' || ~path.indexOf('.')) return next();
    // Save just the query string
    var qs = '';
    if(qsi > -1) qs = req.originalUrl.substr(qsi);
    // Save redirect path
    var redirect = path + '/' + qs;
    // Redirect client
    res.redirect(301, redirect);

    console.log('301 redirected ' + req.originalUrl + ' to ' + redirect);
});

GET変数には常に満足しており、ミドルウェア内に置いても壊れることはありません。

于 2015-12-11T19:06:24.393 に答える
0
/**
 * @param {express.Request} req
 * @param {express.Response} res
 * @param {express.NextFunction} next
 * @return {void}
 */
function checkTrailingSlash(req, res, next) {
    if (req.path.slice(req.path.length-1) !== '/') {
        res.redirect(301, req.path + '/' + req.url.slice(req.path.length));
    } else {
        next();
    }
}
  
app.use(checkTrailingSlash);

結果の例:

/         ==> /
/a        ==> /a/
/a/       ==> /a/
/a/b      ==> /a/b/
/a/b/     ==> /a/b/
/a/b?c=d  ==> /a/b/?c=d
/a/b/?c=d ==> /a/b/?c=d
于 2020-09-08T09:14:43.553 に答える
0

fastifyを使用してルートを処理する場合は、FastifyのignoreTrailingSlashオプションをtrueに設定してみてください。

const fastify = require('fastify')({
  ignoreTrailingSlash: true
})
于 2021-08-24T09:58:10.857 に答える