0

我正在使用 NodeJS 和 Express 开发一个 RESTful API。
我注意到传入的请求有时缺少一些预期的变量,这会导致程序崩溃,说它无法将变量的值设置为一个'undefined'值——因为请求没有到达值。
示例:
应用程序需要 variableY,但正在发送 variableX:

 formData: { variableX: 'valueX' }

该程序期望接收变量Y,代码如下:

const checkVariables = Joi.validate({ 
    variableY: req.body.variableY,
}, schema);

应用程序崩溃并出现以下错误:

TypeError: Cannot read property 'variableY' of undefined

我想了一些方法来处理这个问题,包括在应用程序启动时声明变量并一起使用它们,使用try-catch.
另一种方法是使用if-else, if-chaining, or case-switch,但正如您所理解的那样,我当然正在寻找实现这一目标的最干净的方法。
有任何想法吗?

谢谢你。

** 编辑 **
仅使用对象进行并设法实现结果。一旦试图到达它的任何内部字段,无论如何都会抛出错误,例如:
if(req.body.variableY == undefined){console.log('The expected variable is undefined');} //true

当验证处理“未定义”对象内的字段时:
if(req.body.variableY.dataId == undefined){console.log('The expected variable is undefined');} //crashes
再次引发以下错误:
TypeError: Cannot read property 'variableX' of undefined

在做了一些更多的挖掘之后,发现了这个 Stackoverflow 线程:
如何检查对象属性是否存在与持有属性名称的变量?
尝试使用 hasOwnProperty,但抛出了相同类型的错误:
TypeError: Cannot read property 'hasOwnProperty' of undefined

尝试使用包装变量声明try-catch,仍然没有工作:

try{
    var variableX = req.body.variableX
    var variableXDataId = req.body.variableX.dataId
}
catch(e){
    res.status(400).send('Wrong request error: Please check your request variables and try again');
}

因为这是一个非常基本的验证,应该由大多数 RESTful API 解决(验证您在请求中获得了预期的传入变量,因此程序不会因无法处理的错误而崩溃 - 常见的此类问题的解决方案(预期/意外请求验证)?

谢谢你。

4

3 回答 3

1

您可以采取另一种方法,req.body在到达之前检查checkVariables

let body = req.body;

// data - your req.body
// requiredKeys - is an array of strings , [ key1, key2 ... keyN]  | string[]

     const setKeys = ( data, requiredKeys )=>{

         if( !typeof requiredKeys.length ){
            requiredKeys = [];
         }

         if(requiredKeys.length) requiredKeys.forEach( k =>{

             k = k.replace(/\+/g,'/');

             let keysList = [];

             if( /\/+/g.test(k)){
               keysList = k.split('/');
             }else{
              keysList = [k];
             }

             let [firstKey, ...rest] = keysList;

             if( typeof data[firstKey] === 'undefined' ){
               data[firstKey] = {};
             }

             if( rest.length ){

                data[firstKey] = setKeys(data[firstKey], [rest.join('/')] );

             }

         })

         return data;

      }

let checkedData= setKeys(body, ['variableT','variableP/noname/emptyObj','custom/object/does/not/exist/but/it/will/be/created/here']);

const checkVariables = Joi.validate(checkedData, schema);

更新

您将在下面找到一个工作示例,说明在/(比如说 /usersStatus/:id )请求期间事情应该如何工作:

const express = require('express')
const app = express()
const port = 3000

const setKeys = (data, requiredKeys) => {

  if (!typeof requiredKeys.length) {
    requiredKeys = [];
  }

  if (requiredKeys.length) requiredKeys.forEach(k => {

    k = k.replace(/\+/g, '/');

    let keysList = [];

    if (/\/+/g.test(k)) {
      keysList = k.split('/');
    } else {
      keysList = [k];
    }

    let [firstKey, ...rest] = keysList;

    if (typeof data[firstKey] === 'undefined') {
      data[firstKey] = {};
    }

    if (rest.length) {

      data[firstKey] = setKeys(data[firstKey], [rest.join('/')]);

    }

  })

  return data;

}

/**
 * Mock some data
 */
const getUserData = (req, res, next) => {

  if (typeof req.body === 'undefined') {
    req.body = {};
  }

  req.body = {
    variableY: {
      someName: 23
    },
    variableZ: {
      name: 3,
      type: {
        id: 5,
        typeName: 'something',
        tags: ['a', 'b', 'c']
      }
    }
  };

  console.log('Middleware 1 getUserData');

  next();

}

/**
 * 1. Setup our middleware for checking keys
 *    "requiredKeys" is an array of strings
 */
const middlewareSetKeys = (requiredKeys, wrappedMiddleware) => {

  return (req, res, next) => {

    console.log('Middleware 2 middlewareSetKeys');

    if (typeof req.body === "undefined") {
      console.log('Leaving Middleware 2 since we don\'t have req.body');
      next();
    }

    /**
     *  Update "req.body" with keys that we want to have available
     *  in our next middleware
     */
    req.body = setKeys(req.body, requiredKeys);

    if (typeof wrappedMiddleware === 'function') {

      return wrappedMiddleware.call(this, req, res, next);

    } else {
      next();
    }

  }

}

/**
 *  2. Let's assume a "user status" situation
 *      2.1.  We need userInfo from database
 *      2.2.  Some info won't be retrieved, unless the user accesed some parts of the website to trigger some mechanisms that allows those fields to be exposed, therefore the lack of keys
 *      2.3.  But we know those keys/objects, and we still want to be present so our code won't crash.
 */

// lets call our getUserData
app.get(
  '/', // this path is for some userInfo
  getUserData, // this returns userInfo and appends it to `req.data`
  middlewareSetKeys([
    'userActivity/daily/jobs', // these won't exist in getUserData because the user is lazy and he didn't apply for any JOBS
    'userStatus/active/two-weeks-ago', // these won't exist in getUserData because the user joined two days ago. BUT WE STILL NEED IT coz reazons.
  ]), // We set our desired-later-to-use keys
  (req, res, next) => {

    /**
     * 3. Now our req.body will have our keys 
     *    even if they didn't exist in the getUserData middleware
     */
    console.log('Middleware 3 Your middleware');

    console.log(req.body);
    res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify(req.body, null, 2))

  })

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

于 2018-10-04T05:49:00.353 回答
0

一个解决方案是设置一个默认的空对象来替换父级别的 undefined :

// checking for body.variableX.variableZ with object destructuring ES6
const {body = {}} = request;
const {variableX = {}, variableY} = body;
const {variableZ} = variableX.variableZ;

// or prior ES6
var body = request.body || {};
var variableX = body.variableX || {};
var variableY = variableX.variableY;

// or in a statement
var variableY = request.body && request.body.variableX ? request.body.variableX.variableY : undefined;

基于此,您可以创建自己的函数,例如getValue(request, 'body.variableX.variableY')在未定义任何父级或最终值时返回 null:

// asumes the value in the path is either object or undefined
function getValue(rootObj, path = '') {
    const parts = key.split('.');
    let value = rootObj || {};
    let part;
    while ((part = parts.shift()) && value !== null) {
        value = value[part] || null;
    }
    return value;
};
于 2018-10-05T11:02:52.207 回答
0

您可以使用快速验证器https://www.npmjs.com/package/express-validator 来验证传入的请求。然后将其添加到您的控制器,其中 a、b、c 、d 是您要验证的参数

const nonEmptyFields = ['a', 'b', 'c', 'd'];

  nonEmptyFields.forEach(field => req.assert(field, `${field} cannot be blank`).notEmpty());

  const errors = req.validationErrors();

  if (errors) {
    return res.status(400).send(errors);
  }

要验证字段内的字段,您可以尝试这样做

typeof(req.body && req.body.name !== undefined)
于 2018-10-04T23:06:31.063 回答