NodeJS RESTful API - 如何正确处理“未定义”请求变量?
Posted
技术标签:
【中文标题】NodeJS RESTful API - 如何正确处理“未定义”请求变量?【英文标题】:NodeJS RESTful API - How to handle 'undefined' request variables properly? 【发布时间】:2019-03-09 09:58:51 【问题描述】:我正在使用 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
或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
在做了更多的挖掘之后,找到了这个 *** 线程: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 解决(验证您在请求中获得了预期的传入变量,因此程序不会因出现无法处理的错误而崩溃 - 什么此类问题(预期/意外请求验证)的常见解决方案是什么?
谢谢。
【问题讨论】:
【参考方案1】:解决方案是设置一个默认的空对象来替换父级的 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;
;
【讨论】:
【参考方案2】:您可以使用快速验证器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)
【讨论】:
谢谢linux。那么字段中的字段呢?例如 req.body.variableX.dataId?【参考方案3】:您可以采取另一种方法,在到达checkVariables
之前检查req.body
:
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!`))
【讨论】:
它仅适用于'top'对象,一旦您尝试验证未定义对象内的字段,再次引发以下错误:TypeError: Cannot read property 'variableY' of undefined
找到此帖子后:[@ 987654321@ 尝试使用 hasOwnProperty,但抛出了相同类型的错误:TypeError: Cannot read property 'hasOwnProperty' of undefined
无论如何要克服这个问题?这是验证传入请求的基础。
我已经通过深度对象扫描更新了我的答案。根据您的编辑,您似乎需要已经有一个必填字段/键的列表并根据该列表检查req.body
,您不能只调用任何未定义的键而不让脚本检查它们。以上是关于NodeJS RESTful API - 如何正确处理“未定义”请求变量?的主要内容,如果未能解决你的问题,请参考以下文章
nodejs + mongodb 编写 restful 风格博客 api
RESTful API Design With NodeJS & Restify