这是一个坑爹的Nodejs问题,高手请解释

这是一个坑爹的Nodejs问题,高手请解释

var connect = require(‘connect’); var app = connect(); app.use(connect.logger(‘dev’)); app.use(connect.cookieParser(‘dd’)); app.use(connect.session({secret:‘123’})); app.use(function(req,res,next){ if(req.session.pv){ res.writeHeader(‘200’,{‘Content-type’:‘text/html’}); res.write('views: ’ + req.session.pv); res.end(); req.session.pv ++; }else{ req.session.pv = 1; res.end(‘Refresh’); }

}) app.listen(3000);

这是一个很坑爹的问题,req.session.pv每一次都是双数,如:2,4,6,8但是req.session.pv只是每次自加1而已


4 回复

这个问题确实比较坑爹,涉及到的是Node.js中的异步处理机制。让我们一步一步来分析。

问题描述

代码的目标是创建一个简单的Web服务器,记录用户访问页面的次数。每次用户刷新页面时,req.session.pv会增加1,并且将当前的访问次数显示给用户。

问题所在

问题是,req.session.pv总是显示为偶数(例如2, 4, 6, 8),而不是每次递增1。这显然是不对的,因为根据代码逻辑,req.session.pv应该每次只递增1。

原因分析

问题出在res.write()res.end()的调用顺序上。由于Node.js的异步特性,当res.writeHeader()res.write()被调用时,req.session.pv ++可能还没有执行。因此,下一次请求可能会读取到旧的值,导致看起来像是每次增加了2而不是1。

解决方案

正确的做法是在发送响应之前完成所有必要的操作。具体来说,我们可以在更新req.session.pv之后再发送响应。以下是修改后的代码:

var connect = require('connect');
var app = connect();

app.use(connect.logger('dev'));
app.use(connect.cookieParser('dd'));
app.use(connect.session({ secret: '123' }));

app.use(function(req, res, next) {
    if (req.session.pv) {
        req.session.pv++;
    } else {
        req.session.pv = 1;
    }

    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write('views: ' + req.session.pv);
    res.end();
});

app.listen(3000);

console.log('Server running at http://localhost:3000/');

关键点

  1. 异步处理:Node.js 是事件驱动的,所有的I/O操作都是非阻塞的。这意味着你需要确保在发送响应之前完成所有必要的操作。
  2. 更新session变量:确保在更新req.session.pv之后再进行响应的发送。
  3. 避免重复写入:使用res.writeHead()res.write()res.end()来确保响应头和响应体正确发送。

通过这种方式,可以确保每次请求时req.session.pv都正确递增1。


估计你测试的时候浏览器的每次请求都另带一个favoricon的请求。

+1 把 req.url 打印出来就清楚了。

这个问题确实有点坑爹。问题出在 res.writeres.end 的使用上。res.write 是用来写入响应体的一部分,而 res.end 则是结束响应并发送给客户端。如果你在同一个响应中同时使用了 res.writeres.end,那么 Node.js 会认为这是两个独立的响应部分。

在你的代码中,res.writeres.end 被分开调用,这会导致请求处理逻辑没有按预期执行。具体来说,每次请求时,res.end 会先于 req.session.pv++ 执行,导致计数器没有正确递增。

你可以将 res.writeres.end 合并为一个语句来修复这个问题,或者使用 res.end 的可选参数来传递响应体内容。

下面是修改后的代码示例:

var connect = require('connect');
var app = connect();
app.use(connect.logger('dev'));
app.use(connect.cookieParser('dd'));
app.use(connect.session({ secret: '123' }));

app.use(function(req, res, next) {
    if (req.session.pv) {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end('views: ' + req.session.pv++);
    } else {
        req.session.pv = 1;
        res.end('Refresh');
    }
});

app.listen(3000);

在这个修改后的代码中,我们使用了 res.end 的可选参数来传递响应体内容,并且把 req.session.pv++ 放在 res.end 前面,这样可以确保每次请求时 req.session.pv 都能正确递增。

回到顶部