NodeJS RESTful API-如何正确处理“未定义”的请求变量?

时间:2018-10-04 05:13:32

标签: node.js express request undefined req

我正在使用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-elseif-chainingcase-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线程:
How to check if object property exists with a variable holding the property name?
使用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都应该解决的非常基本的验证(验证您是否在请求中获得了预期的传入变量,所以该程序不会因无法处理的错误而崩溃-什么?此类问题(预期/意外请求验证)的通用解决方案是什么?

谢谢。

3 个答案:

答案 0 :(得分: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}!`))

答案 1 :(得分: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)

答案 2 :(得分:0)

一种解决方案是设置默认的空对象,以替换父级未定义的对象:

// 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;
};