I'm trying to set params for routing in my webapp by string:
app.param('id', /^\d+$/);
I need it to do for better routing setting, like
app.get('/post/:id', show);
But I'm getting next error:
throw new error ('invalid param() call for ' + name + ', got ' + fn)
What am I doing wrong?
From your comments, it seems evident that you forgot to include the part of the example code which allows support for Regular Expressions in app.param. The documentation has the following code before app.param('id', /^\d+$/);:
app.param(function(name, fn){
if (fn instanceof RegExp) {
return function(req, res, next, val){
var captures;
if (captures = fn.exec(String(val))) {
req.params[name] = captures;
next();
} else {
next('route');
}
}
}
});
Include that, and you should be fine.
Alternatively, you could just use the express-params module, that includes that code for you.
Related
app.get('/dir/:dirname', (req, res) => {
const isFile = fileName => {
return fs.lstatSync(fileName).isFile();
}
var retString = '';
var dir = `d:\\${req.params.dirname}`;
console.log(dir);
retString+='<table>';
fs.readdirSync(dir).map(fileName => {
console.log(fileName);
//retString+=`<tr><td>${dir}</td><td><a href='${path.join(dir, fileName)}>${fileName}</a></td></tr>`;
retString+=`<tr><td>${dir}</td><td>${fileName}</td></tr>`;
}).filter(isFile);
retString += '</table>';
res.send(retString);
res.end();
});
it delivers the file names, but runs into error after the end of the list.
What did I miss out?
Your .map() was not returning anything from the callback. That means you pass an array of undefined to .filter() which then tries to pass undefined to fs.lstatSync() which causes your error.
You don't call both res.send() and res.end() because res.send() already ends the response so when you then call res.end() again, that can cause an error.
res.end() is used when you are using res.write() which can be called multiple times and does not end the response.
Also, your .filter(isFile) is not doing anything useful. You're building the HTML before you filter and then not saving of using the result of the filter. You need to filter before you map as in:
fs.readdirSync(dir).filter(isFile).map(...)
Here's how your code looks using asynchronous file I/O and using the withFileTypes option and inserting some error handling:
app.get('/dir/:dirname', async (req, res) => {
try {
let retString = '';
// this is potentially dangerous as ANY user of this server
// can browse anywhere on your d: drive
const dir = `d:\\${req.params.dirname}`;
console.log(dir);
retString += '<table>';
let entries = await fs.promises.readdir(dir, { withFileTypes: true });
for (let entry of entries) {
if (entry.isFile()) {
console.log(entry.name);
retString += `<tr><td>${dir}</td><td>${entry.name}</td></tr>`;
}
}
retString += '</table>';
res.send(retString);
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
Other Issues:
This is potentially dangerous code as it allows anyone with access to your server to browse anywhere they want on your d: drive with no restrictions
This presumably should return a fully formed web page, not just an HTML table.
Thank you jfriend - this did it.
I am beginning to understand node.js
Dont worry about security - this is running on local network only and will be limited before getting to be used elsewere.
I am aware of this leak.
But thank you for this hint as well, because others might need this reminder.
In the code below, I am trying to get a value from a 'nodehun' method called spellSuggestions. In the documentation I'm told the syntax to use this method is as follows: dict.spellSuggestions(w, handleMisspellings);
where w is a list of words and handleMisspellings is a function (which is posted below). I can see the output on the console for handleMisspellings, but for the life of me, I cannot return or find a way to return a variable from this call: [dict.spellSuggestions(w, handleMisspellings);]. After setting a var equal to 'dict.spellSuggestions(w, handleMisspellings);' the return value is undefined. Please help!
var debugFlag = process.argv.indexOf('debug') > -1;
var nodehun = require('./../build/' + (debugFlag ? 'Debug' : 'Release') + '/nodehun');
var fs = require('fs');
var dict = new nodehun(fs.readFileSync(__dirname+'/dictionaries/en_US.aff'),fs.readFileSync(__dirname+'/dictionaries/en_US.dic'));
//var words = ['original', 'roach', 'erasee', 'come', 'consol', 'argumnt', 'gage',
// 'libary', 'lisence', 'principal', 'realy', 'license', 'suprise', 'writting'];
var handleMisspellings = function(err, correct, suggestions, origWord, callback) {
if (err) throw err;
if (correct) {
console.log(origWord + ' is spelled correctly!');
}
else {
console.log(origWord + ' not recognized. Suggestions: ' + suggestions);
}
var value = {
err: err,
correct: correct,
suggestions: suggestions,
origWord: origWord
};
console.log('VALUE+++++: ' + value);
return value;
}
var foo = function(words) {
words.forEach(function(w) {
dict.spellSuggestions(w, handleMisspellings);
some = dict;
console.log(JSON.stringify(some, null, 2));
});
}
module.exports = {
foo: foo
}
Thanks Dave. I eventually discovered the practical use of callback functions. For each method that contained data that I desired to access outside of the method, I declared an individual function to wrap the method. The function accepted two input arguments. The first was the input variable to drive the method call. The second was literally 'callback'. Inside the method, I would perform whatever operation I wanted to package the data into a JSON object before returning any desired data with 'return callback(var)'. In the call of the created wrapper function, I would actually call the function using the input variable of choice to drive the method in the function definition, and pass 'function(return_variable)' as the second argument. This creates a new method in which the desired data may be accessed or even again called back. The final code I desired performs a for loop on each element of a list of words, creates metadata about those words, and appends the unique data for each word to each word in a single array. The final array is a single object which contains all input words, and all data about those words. It required 4 individual functions (one of which was recursive), and a function call. Please see the code snippet of the function described above [doCall]. Note the use of the code begins at the call of 'analyze' [which is commented out here] and works its way up to each previous function declaration. I hope this helps someone else in the future to understand the functional use of 'callbacks'. Please ask if you have any questions, and Thanks again =D.
function doCall(word, callback) {
dict.spellSuggestions(word, function(err, correct, suggestions, origWord) {
if (err) throw err;
// if (correct)
// console.log(origWord + ' is spelled correctly!');
// else
// console.log(origWord + ' not recognized. Suggestions: ' + suggestions);
val = {
err: err,
correct: correct,
origWord: origWord,
suggestions: suggestions
}
return callback(val);
});
}
function addMember(array, index, callback){
doCall(array[index], function(val){
// console.log(val);
// console.log(index);
// console.log(array[index]);
// console.log(val.origWord);
array[val.origWord] = val;
// console.log(array[val.origWord]);
index = index + 1;
return callback(array, index);
});
}
function Loop(array, index, callback) {
addMember(array, index, function(array2, index2){
// console.log(index);
// console.log(index2);
if(index2 === array2.length) {
return callback(array2);
}
else{
Loop(array2, index2, callback);
}
});
}
function analyze(array, index, callback){
Loop(array, index, function(complete_array){
console.log('!!!!!!!!!!!!!!!!!' + complete_array);
return callback(complete_array);
});
}
/*
analyze(words, 0, function(complete_array){
// for(i = 0; i < complete_array.length; i++) {
console.log(complete_array);
// }
});
*/
module.exports = {
analyze
}
I'm trying to make an implementation using nodejs with jsftp libraries.
This is my piece of code:
function sftpOperation(bigstring, res) {
values = bigstring.split("#"),
connProperties = {
desthost: values[0],
destuser: values[1],
destpass: values[2]
},
destpath=values[3],
sourfile=values[4],
client = new jsftp({host:connProperties.desthost,
port:22,
user:connProperties.destuser,
pass:connProperties.destpass,
debugMode: true});
client.auth(connProperties.destuser, connProperties.destpass, function(hadErr){
if (!hadErr){console.log("auth succesfull");}
});
client.put('/home/nagios/out','/home/jboss/outnew', function(hadErr){
if (!hadErr){
res.end("OK");
}
else
res.end("KO : " + err);
});
}
The callback function didn't return nothing... the if statement after callback is completely skipped.
Looking at properties for object "client", I see that Command queue properties contain one object (CommandQueue[1] with value "action jboss", where 'jboss' is the value for connProperties.destuser).
Can someone help to understand what's going wrong with this?
I have been working with Parse Cloud Code, and I have not achieved setting default values for my classes. Right now I am doing this:
Parse.Cloud.beforeSave('MyClass',function(request, response){
//The description should only have 200 characters
if(request.object.get('description')){
var des = request.object.get('description');
if(des.length>200){
request.object.set("description", des.substring(0, 197) + "...");
}
}
//let's make a default value
if(typeof request.object.get('active') === 'undefined'){
request.object.set('active',false);
}
response.success();
});
When I upload this function to the Cloud Code, and try to create a new object from the dashboard it wont have the default value active = false.
I don't know what's going on. Has somebody achieved this before?
My code is very similar to the one in the Parse docs. They say this could be done like this:
Parse.Cloud.beforeSave("Review", function(request, response) {
var comment = request.object.get("comment");
if (comment.length > 140) {
// Truncate and add a ...
request.object.set("comment", comment.substring(0, 137) + "...");
}
response.success();
});
However, for me it doesn't want to work.
Wohoo! I did it!
Nowhere in the documentation is said that if you want to do so, you must return the object you edited in the response.success().
Solution:
Parse.Cloud.beforeSave('MyClass',function(request, response){
//The description should only have 200 characters
if(request.object.get('description')){
var des = request.object.get('description');
if(des.length>200){
request.object.set("description", des.substring(0, 197) + "...");
}
}
//let's make a default value
if(typeof request.object.get('active') === 'undefined'){
request.object.set('active',false);
}
response.success(request.object); //this is the line that changes
});
I am using Sails' ORM (Waterline). I have written a geturl service that should return the url of several models/actions in my app. I am currently calling this service inside my templates.
(As I am alone to develop this, don't hesitate to warn me if this design pattern is wrong)
Now it occurs that Waterline's .find() method is asynchronous (as it should). I always use callbacks to do things when inserting or fetching things in database.
Now I have seen everywhere that I cannot return any data from asynchronous methods. As a consequence I am puzzled because I want to create this [damned] service to centralize the URL management.
Here is my current code:
module.exports = {
variete: function(id_objet) {
var string = '/default_url';
return onvariete(id_objet, function (err, url) {
if (err) {
sails.log.error('Error : ', err);
} else {
return url;
}
});
}
};
function onvariete(id_objet, next) {
var url = '/';
return Variete.findOne({id:id_objet}).exec(function (err, v) {
sails.log.info('URL Variety : '+ v.nom + ' / ' +id_objet + ' / ' + v.slug);
if (err) {
sails.log.error('Error : ' + v.nom + ' / ' + err);
// Do nothing.
return next(new Error('Variete error'), undefined);
} else if (!v) {
return next(new Error('Variete not found'), undefined);
} else if (!v.slug) {
// variete doesn't have a slug field
// we redirect to /v/:id
url += 'v/' + v.id;
return next (null, url);
} else {
// Ok variete has got a slug field
sails.log.info('GOT A SLUG! ' + v.slug);
url += 'variete/' + v.slug;
return next (null, url);
}
});
}
I made a static object that embeds my geturl service, and then inside a Jade template:
a(href="#{s.geturl.variete(ann.variete.id)}" title="#{ann.variete.name}") #{ann.variete.name}
And I can get something like:
<a title="Tomate Coeur de Boeuf" href="undefined">Tomate Coeur de Boeuf</a>
Thank you by advance.
The solution vas to write a .url(bool) instance method. See how to write instance methods in Sails / Waterline.
This way I directly access this method from my template : a(href="#{ann.variete.url()}" title="#{ann.variete.name}") #{ann.variete.name}.
Done!