Copied directly from Braintree's tutorial, you can create a client token with a customer ID like this:
gateway.clientToken.generate({
customerId: aCustomerId
}, function (err, response) {
clientToken = response.clientToken
});
I declare var aCustomerId = "customer" but node.js closes with the error
new TypeError('first argument must be a string or Buffer')
When I try to generate a token without the customerId, everything works fine (though I never get a new client token but that's another question).
EDIT: Here is the complete test code as requested:
var http = require('http'),
url=require('url'),
fs=require('fs'),
braintree=require('braintree');
var clientToken;
var gateway = braintree.connect({
environment: braintree.Environment.Sandbox,
merchantId: "xxx", //Real ID and Keys removed
publicKey: "xxx",
privateKey: "xxx"
});
gateway.clientToken.generate({
customerId: "aCustomerId" //I've tried declaring this outside this block
}, function (err, response) {
clientToken = response.clientToken
});
http.createServer(function(req,res){
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(clientToken);
res.end("<p>This is the end</p>");
}).listen(8000, '127.0.0.1');
Disclaimer: I work for Braintree :)
I'm sorry to hear that you are having trouble with your implementation. There are a few things that might be going wrong here:
If you specify a customerId when generating a client token, it must be a valid one. You do not need to include a customer id when creating a client token for a first time customers. Typically you would create create a customer when handling the submission of your checkout form, and then store that customer id in a database for use later. I'll talk to our documentation team about clarifying the documentation around this.
res.write takes a string or a buffer. Since you were writing response.clientToken, which was undefined because it was created with an invalid customer id, you were receiving the first argument must be a string or Buffer error.
Some other notes:
If you create a token with an invalid customerId, or there is another error processing your request, response.success will be false, you can then inspect the response for the reason why it failed.
You should generate your client token within the http request handler, this will allow you generate different tokens for different customers, and to better handle any issues that result from your request.
The following code should work, provided you specify a valid customerId
http.createServer(function(req,res){
// a token needs to be generated on each request
// so we nest this inside the request handler
gateway.clientToken.generate({
// this needs to be a valid customer id
// customerId: "aCustomerId"
}, function (err, response) {
// error handling for connection issues
if (err) {
throw new Error(err);
}
if (response.success) {
clientToken = response.clientToken
res.writeHead(200, {'Content-Type': 'text/html'});
// you cannot pass an integer to res.write
// so we cooerce it to a string
res.write(clientToken);
res.end("<p>This is the end</p>");
} else {
// handle any issues in response from the Braintree gateway
res.writeHead(500, {'Content-Type': 'text/html'});
res.end('Something went wrong.');
}
});
}).listen(8000, '127.0.0.1');
Related
I am trying to update a collection in mongoDB after the user finishes some tasks. However, whenever I attempt to save the information and update mongo, I'm getting the error POST http://localhost:3000/updateSurvey/634124db6f 400 (Bad Request). Any ideas why my code isn't functioning correctly?
Backend js script
app.post('/updateSurvey', async (req, res) => {
try {
await client.connect();
var db = client.db('Admin_Db');
var collection = db.collection('Survey');
await collection.findOneAndUpdate({"_id": ObjectId(req.body.id)}, {completion: req.body.completion});
res.send("updated");
} catch (error) {
console.log(error);
res.status(400).send(error);
}
});
Main.js (this is how I am fetching the data from mongo)
fetch("http://localhost:3000/updateSurvey", {
method:'POST',
headers:{'Content-Type': 'application/json'},
body: JSON.stringify({id: surveyID, completion: true})})
.then(response=>{response.text()})
.catch(function(err){console.log("Fetch Problem: " + err)});
You don't have a route http://localhost:3000/updateSurvey/634124db6f exposed on your server. Therefore the 404 error. Since you are using a post call, just pass the surveyID in your body when making the post call using fetchAPI instead of sending it as a query param.
And make sure http://localhost:3000/updateSurvey is the route to which your are sending your data, without the surveyId in the URL.
Edit
Edits made as per request received in comments.
collection.findOneAndUpdate({"_id": id)}, {completion: req.body.completion});
should be:
collection.findOneAndUpdate({"_id": new ObjectId(id))}, {completion: req.body.completion});
_id is of type ObjectId in MongoDB. You are passing id as string. This should most likely be the error from what I can gather by the code shared in your question. You can cast a string to ObjectId by using the ObjectId class provided by the Mongo Driver for node. Even mongoose provides this class.
I want to send my token and my user's role to my login.component.ts.
When I was trying to find the problem, in my research I came across someone's suggestion to use
res.write(foo1)
res.write(foo2)
res.end
Instead of
res.send(foo1)
res.send(foo2)
But that didn't work.
I then tried using this to test it:
res.write(foo1)
res.end()
But this is giving me an error:
events.js:174
throw er; // Unhandled 'error' event
^
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string or Buffer. Received type object
at write_ (_http_outgoing.js:595:11)
at ServerResponse.write (_http_outgoing.js:567:10)
at User.findOne (C:\Users\notan\GitHub\se3316-notansandwich-lab5\server\controllers\user.controller.js:46:33)
at C:\Users\notan\node_modules\mongoose\lib\model.js:4604:16
at C:\Users\notan\node_modules\mongoose\lib\query.js:4348:12
at process.nextTick (C:\Users\notan\node_modules\mongoose\lib\query.js:2850:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
Emitted 'error' event at:
at C:\Users\notan\node_modules\mongoose\lib\model.js:4606:13
at C:\Users\notan\node_modules\mongoose\lib\query.js:4348:12
at process.nextTick (C:\Users\notan\node_modules\mongoose\lib\query.js:2850:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
This is my user.controller.js, which I use in my route.js which is used in my sever.js
const User = require('../models/user.model')
const jwt = require('jsonwebtoken')
exports.user_create = function (req, res, next) {
let user = new User(
{
_id: Math.random().toString(36).substr(2, 5), // generate a random ID
email: req.body.email,
password: req.body.password,
firstName: req.body.firstName,
lastName: req.body.lastName,
role: "user"
}
);
user.save(function (err, registeredUser) {
if (err) {
return next(err);
}
else {
let payload = { subject: registeredUser._id}
let token = jwt.sign(payload, 'secretKey')
res.status(200).send({token})
}
})
}
exports.user_login = function (req, res, next) {
let userData = req.body
User.findOne({email: userData.email}, (err, user) => {
if (err) {
console.log(err)
}
else {
if (!user) {
res.status(401).send('Invalid email')
}
else if (user.password !== userData.password) {
res.status(401).send('Invalid password')
}
else {
let payload = {subject: user._id}
let token = jwt.sign(payload, 'secretKey')
//res.status(200).send({token})
res.status(200).write({token})
//let role = this.user.role
// res.status(200).write({role})
res.end()
}
}
})
}
Using this works
res.status(200).send({token})
But this does not
res.status(200).write({token})
res.end()
In response to the title of your question:
Can I use res.send(foo) twice?
No, you cannot call that twice for the same request.
See the second part of the answer since the OP changed their question after I wrote this first part
In Express, you can only use res.send() or res.json() or res.end() once per request. When you execute those, it sends the request. If you try to send more on the same request, it will do nothing and will show a warning in Express.
res.write() can be called more than once, then followed by res.end() when you are finally done with the request.
In your example:
res.status(200).send({token})
res.end()
The res.send() already calls .end() for you so trying to call it again is considered an error because the request has already been sent.
FYI, .status(200) is not necessary. The default status is already 200 so res.send({token}) will already have a 200 status.
More Recent Answer for the Modified Question
Now that you've completely changed the question to be about res.write({token}), that does not work because res.write() requires a String or a Buffer as an argument and you were giving it an object. You would have to manually convert the object to JSON yourself:
res.type('application/json');
res.write(JSON.stringify({token}));
res.end();
And note that this also sets the appropriate content type. If your object is large with res.write() you may also have to pay attention to the write buffer being full and listen for the drain event. res.write() is a much lower level facility (it's at the http level, not at the Express level) than the Express functions for sending data.
Built into Express, you can use res.send() or res.json() which are Express methods that will both that you passed an object, automatically convert it to JSON for you and set the content type to JSON also. It will also handle any buffer full issues in the write stream too.
res.send({token});
or, I prefer to be more explicit in my Express code with:
res.json({token});
And, if you're trying to send multiple pieces of data, you can put them into the same object:
res.json({token, role});
Calling res.status(200).send({ token }) ends the request and sends the object back to the client...
Since the request is now ended... Calling res.end() generates the error...
You'd usually use res.end if u were piping some data (usually binary) after several res.write to close the pipe...
For more info... checkout Express' docs in the response object...
https://expressjs.com/en/api.html#res
Also... U can't send an object using res.write...
From your error... it says that it inly accepts a string or buffer. If u want to send plain objects... res.send or res.json would be more appropriate...
I found the solution: I can send multiple things in an res.send(...)
res.status(200).send({token, role})
If you want to use res.write(argument) you have to pass the argument as a string or Buffer, just like the error message says. To get the same effect just convert your response object to a string:
res.status(200).write(JSON.stringify({token}))
Iam trying to create a post call which basically takes a file(eg img,pdf file) and then it need to upload in to object storage on bluemix.I was able to authenticate and get the token and create the authurl.I just need to pass file which we upload along with the url.But Iam out of ideas how I can get the file uploaded from postman to be passed to that url with in the post call..Below is my code
app.post('/uploadfile',function(req,res){
getAuthToken().then(function(token){
if(!token){
console.log("error");
}
else{
var fileName = req.body.file;
console.log("data",file);
console.log(SOFTLAYER_ID_V3_AUTH_URL,"url");
var apiUrl = SOFTLAYER_ID_V3_AUTH_URL + config.projectId + '/' + containerName + fileName ;
url : apiurl,
method :'PUT',
headers :{
'X-Auth-Token': token
},function(error, response, body) {
if(!error && response.statusCode == 201) {
res.send(response.headers);
} else {
console.log(error, body);
res.send(body);
}
}
}
})
});
Can someone help here.
Since you're using Express, you should use something like:
https://www.npmjs.com/package/express-fileupload
https://github.com/mscdex/connect-busboy
https://github.com/expressjs/multer
https://github.com/andrewrk/connect-multiparty
https://github.com/mscdex/reformed
Without a body parser that handles file uploads you will not be able to get the uploaded file in the Express request handler.
Then, you need to pass the uploaded file to the request that you're making.
For that you should use this module:
https://www.npmjs.com/package/bluemix-object-storage
There is no need to reinvent the wheel when there are tested and eay to use solutions available. Especially when you're dealing with sensitive information like API keys and secrets I would not advice you to implement your own solution from scratch, unless you really know what you're doing. And if you really know what you're doing, then you don't need to seek advice for things like that.
Here is the official Object Storage SDK for Node.js:
https://github.com/ibm-bluemix-mobile-services/bluemix-objectstorage-serversdk-nodejs
Connect to Object Storage:
var credentials = {
projectId: 'project-id',
userId: 'user-id',
password: 'password',
region: ObjectStorage.Region.DALLAS
};
var objStorage = new ObjectStorage(credentials);
Create a container:
objstorage.createContainer('container-name')
.then(function(container) {
// container - the ObjectStorageContainer that was created
})
.catch(function(err) {
// AuthTokenError if there was a problem refreshing authentication token
// ServerError if any unexpected status codes were returned from the request
});
}
Create a new object or update an existing one:
container.createObject('object-name', data)
.then(function(object) {
// object - the ObjectStorageObject that was created
})
.catch(function(err) {
// TimeoutError if the request timed out
// AuthTokenError if there was a problem refreshing authentication token
// ServerError if any unexpected status codes were returned from the request
});
I'm currently working on a angular + sails project. I'm using json web tokens for auth. It works fine but I wanna set a new token for every validated request that my angular app does.
This is my auth policy
passport.authenticate('jwt', function (error, user, info) {
if (error) return res.serverError(error);
if (!user)
return res.send({
message: info.message,
code: info.code,
tokenError: info.name
});
// The token is ok past this line
// I check the user again
User.findOne({ email: user.email }, function (err, thisUser) {
if (err) { return res.send(err); }
if (!thisUser) {
// send a bad response
}
req.user = user;
// This is the new token that I wanna send to the frontend
var newToken = AuthService.createToken(thisUser);
next();
});
})(req, res);
With this policy I can create the new token, but then I would need a way to include this token in every response, this Is the point where I'm stuck.
I gues I could do it manually in every controller action, but this is want I want to avoid
The best way to standardize your responses in Sails is to use the custom responses feature. In short, instead of calling res.send() or res.json() in your controller actions, call res.ok() instead, and then customize the api/responses/ok.js file that is generated with every new Sails app. This is the same response that Sails blueprints use as well!
In your case, you'd want to save the token onto the request object (e.g. req.token) in your policy code, then use that property in your logic inside of ok.js.
I have a REST endpoint that is being submitted with a JSON object (User) and I just set the corresponding mongo record to that JSON object. This saves me the trouble of updating schema changes in the service method and the endpoint leaving just the Mongoose model to update.
What would be a more secure way of doing this, if any?
Example User JSON
{
'fname': 'Bill',
'lname': 'Williams',
'email': 'bill#billwilliams.com',
'settings': {
'strokeColor': '#FF0000'
}
}
From my Angular service
Update: function(my_user) {
return $http.put('http://api.domain.com/v1/api/users/' + _user.id, {
user: my_user,
token: window.localStorage['token']
});
}
My REST endpoint in Node
api.route('/users/:user_id')
.put(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
userData = req.body.user;
if (user) {
//-- This is potential trouble area?
User.update({'_id': user._id}, {$set: userData});
user.save(function(err) {
res.json({
success: true,
message: 'User updated'
});
}); //-- end findById()
}); //-- end /users/:user_id put() route
Have a look at Jsonwebtoken.
It basically works like this:
Create a REST endpoint that lets users aquire a token with a certain
payload (id for example)
Secure the relevant part of your api with the Jsonwebtoken middleware (if using express as the webserver)
User adds the token to every request header (by using $httpInterceptor)
Token is checked on the server side before the request reaches your API
Tokens may expire after a certain time (useful when users needs to register first) which adds additional security.