求相关中间件
发布于 9 年前 作者 showen 4591 次浏览 最后一次编辑是 8 年前 来自 问答

不知道express里面有没有类似restify中queryParser的中间件,效果就是所有传过来的参数不管是路由里面的、get、post的都会再req.params里面。

15 回复

自己写一个,应该不会超过3行

@i5ting 三行写了一下:

App.use ( req, res, next )->
   _.extend req.params, req.body, req.query
   next()

但是发现了一个问题:req.params在当前中间件修改生效了, 但是在下一个中间就被重置了。我测试了一下给req添加一个key例如req.test='test’是能够被传递到下一个中间件的。请指正。

@showen 这句放的位置有关系的

@i5ting 确定放到了最前面,而且刚才在它之后写了一个中间件测试了是传不过来的。

@showen 不对,是在解析之后,也就是路由前面

@i5ting 大哥你测试了没 我连续写两个中间件req.params修改值都没有传给下一个中间件

上面的讨论可能版本不同?
以下仅供参考

app.use(function(req, res, next){
  Object.defineProperty(req, 'params', {
    set: function(val){
      this._allParams = _.extend(val, req.query, req.body);
    },
    get: function(){
      return this._allParams;
    }
  });
  next();
});

@showen 代码发出来看看?我光看你的描述的话,得出的结论跟 @i5ting 一样

@William17 赞 你这种能生效 我用的node版本是4.2.2的 express是4.13.1 是版本的问题吗 能不能详细解释一下呢 还有就是发现一个小问题 在Object.defineProperty之后打印req.params是undefined 但是在Object.defineProperty之前打印就是一个空Object:{} 很诧异

@alsotang 就上面三行代码 问题现在可以简单描述为:上一个中间件修改了req.params在下一个中间件不生效 @William17 给的解决方案能生效

@William17 我看了restify的queryParser源码也是直接修改的req.params如下:

function queryParser(options) {
        if (!options)
                options = {};
        assert.object(options, 'options');
        function parseQueryString(req, res, next) {
                if (!req.getQuery()) {
                        req.query = {};
                        return (next());
                }

                req._query = req.query = qs.parse(req.getQuery());
                if (options.mapParams !== false) {
                        Object.keys(req.query).forEach(function (k) {
                                if (req.params[k] && !options.overrideParams)
                                        return (false);

                                req.params[k] = req.query[k];
                                return (true);
                        });
                }

                return (next());
        }

        return (parseQueryString);
}

@showen

A

我只是稍微看了express当前最新的部分代码,其它版本没看过,所以不太清楚以前的版本是不是可以直接修改req.params
对于当前最新的代码,我解释一下
/lib/router/index.js#L135

proto.handle = function handle(req, res, out) {
  // ...

  // 每进入一个Router,保存上一级Router的params
  // 这个parentParams在下面用来的mergeParams
  // 主要用于类似下面这种情况
  // var router1 = require('express').Router();
  // var router2 = require('express').Router({mergeParams:true});
  // router1.use('/api/:version',router2);
  // router2.get('/user/:id',function(req, res, next){
  //    // 这里会有上一级Router的param
  //    console.log(req.params.version);
  // })
  var parentParams = req.params;
 
  // ...

  next();

  function next(err) {
    // ...
    // 遍历这个Router的layer
    while (match !== true && idx < stack.length) {
      layer = stack[idx++];
      // ...
	  // 创建新的req.params
	  // 如果有mergeParams=true,则把父级的params合并进来
      req.params = self.mergeParams
        ? mergeParams(layer.params, parentParams)
        : layer.params;
     // ...
  }
  
 }

可以看到,每一个layerreq.params都会指向新的对象,所以在如果在某个layer修改了req.params所引用的对象的属性,这种改变不会影响到其它的layer。 因此,我们在req上动手脚。

B

在Object.defineProperty之后打印req.params是undefined 但是在Object.defineProperty之前打印就是一个空Object:{}

之前是{}是因为express一开始给它赋的值,之后是undefined是因为这时候返回的值是我们getter函数里返回的req._allParams, 而这个我们没初始化定义

所以,初始化一下就好了

app.use(function(req, res, next){
   // 初始化
  req._allParams = req.params;
  Object.defineProperty(req, 'params', {
    set: function(val){
      this._allParams = _.extend(val, req.query, req.body);
    },
    get: function(){
      return this._allParams;
    }
  });
  next();
});

@showen @William17 @i5ting 这种情况下,我建议还是不要覆盖 req.params 了。换个名字,req._params 之类的。

@William17 不甚感激!

@alsotang 为了兼容别人的代码。。。

回到顶部