我正在使用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
进行更多挖掘之后,发现以下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都应该解决的非常基本的验证(验证您是否在请求中获得了预期的传入变量,所以该程序不会因无法处理的错误而崩溃-什么?此类问题(预期/意外请求验证)的通用解决方案是什么?
谢谢。
答案 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;
};