web-dev-qa-db-ja.com

Expressですべての末尾のスラッシュをグローバルにリダイレクトします

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 ウィジェットがあり、myapp.com/lessonsmyapp.com/lessons/にアクセスすると2つの異なるページが表示されるという奇妙な動作に気付きました(そのうちの1つにコメントがありました)以前にDisqusに追加しましたが、もう1つにはコメントがありません)。

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

ありがとう

34
Michael

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

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();
});
54
Tolga Akyüz

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

リダイレクト先URLの末尾に元のクエリを追加することで問題を解決する、提案されたミドルウェアの改善されたバージョンを次に示します。

app.use(function(req, res, next) {
    if (req.path.substr(-1) == '/' && req.path.length > 1) {
        var query = req.url.slice(req.path.length);
        res.redirect(301, req.path.slice(0, -1) + query);
    } else {
        next();
    }
});

注: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/に等しくなり、リダイレクトは回避されます。

78
Akseli Palén

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

以下でインストールします:

$ npm install connect-slashes

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

24
Roi Avinoam

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

router.use(function(req, res, next) {
    if (req.originalUrl != req.baseUrl + req.url) {
        res.redirect(301, req.baseUrl + req.url);
    }
    else
        next();
});

これは翻訳します:

/page ==> /page/
/page?query=value ==> /page/?query=value
8
ShortFuse

一発ギャグ:

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
3
Ronen Teva

上記の回答は多くの場合に機能しますが、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変数には常に満足しており、ミドルウェアに入れても壊れません。

0
user2687646