Express/node.js 204 HTTP code response issue - node.js

Here is my code:
.put(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
if(err) return res.send(err);
user.dateEdited = new Date();
user.save(function(err) {
if(err) return res.send(err);
return res.status(204).json(customHTTPcodeReponses.updated(user))
});
});
});
Part of my middleware called customHTTPcodeReponses
updated: function(data) {
return {
code: 204,
status: 'Success',
message: 'Resource updated (or soft deleted)',
data: data
};
}
I as figured out, 204 is not supposed to return any data, so I am not getting any back.
But I would like to have this data to see what was really changed. How could I hande response code then?
Have in mind that if I use
res.status(200).json(customHTTPcodeReponses.updated(user))
data is shown.
If you need some extra explanation, please ask.

yes, you are correct. This http status not allowed messages because it means "No Content". If you send content, use other statuses. For details look at document: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
There is part: "The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields."
If you want to send not content but metadata with addictional info, in this document there is part : "The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.". I think it is formal answer for your question.
If you not so formal, use status '200' with metadata as content.
edit:
to send data in header :
res.header(field, [value])

It is simple. If you need some data back, just use return code 200 and return that data. If you don't need send back any data, use 204. You can set header with 204 to indicate operation status or url like Location with return code 201, but I am not sure this is a good idea.

yes if you have to send response message the use 200 or you can also use 404
because of 204 return no message in body

Related

How to get response body from Express.js server using Supertest?

I started to write some tests for my application and I have issues to read/get response from the server. I tried many things but nothing really worked, can someone help me please ?
// /api/checkCreds
exports.checkCreds = async function(req, res){
//validation
if(!await User.checkCreds(req.body.username, req.body.password)){
var result = {error: true, data: "Incorrect"}
res.sendStatus = 401;
return res.send(JSON.stringify(result));
}
If credentials sent to the server aren't matching, return a response with "Incorrect" message back to the user.
In the test I'm trying to get data from the server to check if properties are matching the expected output.
//test.js
it("We should fail with HTTP code 401 because incorrect data is passed (username='incorrect' password='incorrect')", function(done){
supertest(app)
.post('/api/checkCreds')
.send({username: 'incorrect', password: 'incorrect'})
.expect({error: true, data: "Incorrect"})
.expect(401, done);
});
When ran, test fails because expected properties are different from the response sent by the server, which is an empty object {}.
Any help is appreciated.
You may try changing your first expect to see if you can coax supertest into showing you the actual body that it's comparing to. For example, expect('')
If that doesn't work, there's a version of expect that accepts a function. In that function, you should be able to print out what you are getting in the response body, ie. console.log(res).
It may be that there's some confusion with the JSON return type-- I haven't used that directly. You could try expecting JSON.
Finally, there's a strange paragraph in the documentation that I don't think applies, but I thought I'd mention:
One thing to note with the above statement is that superagent now sends any HTTP error (anything other than a 2XX response code) to the callback as the first argument if you do not add a status code expect (i.e. .expect(302)).
While trying to fix my issue, I noticed that in the HTTP response, Content-Type header was set to text/plain and my server was returning JSON, so that probably was the thing that confused supertest.
I think that res.send() sets the header to text/plain by default and I had to manually set the header value to application/json by using res.type('json'). At that point I was able to read the response body without an issue.
I also learned that res.json() sets the Content-Type header to application/json by default, so you don't need to do it manually like with res.send().
Working code:
// /api/checkCreds
if(!await User.checkCreds(req.body.username, req.body.password)){
var result = {error: true, data: "Incorrect"}
return res.status(401).json(result);
}
//test.js
it("We should fail with HTTP code 401 because incorrect data is passed (username='incorrect' password='incorrect')", function(done){
supertest(app)
.post('/api/checkCreds')
.set('Content-type', 'application/json')
.send({username: 'incorrect', password: 'incorrect'})
.expect(401)
.expect(function(res){
console.log(res.body);
})
.end(done);
});
Feel free to correct me if I stated something that isn't quite right.

NodejS : Error: Can't set headers after they are sent to the client

I am quite new to node and am currently encountering an error that seems pretty common: Error: Can't set headers after they are sent to the client.
I've read a few threads but cannot wrap my head around this one, here is my code so far:
router.get('/:id', (req, res) => User.findAll({
where : {
id: req.params.id,
},
attributes : {
exclude : ['updatedAt'],
}
})
.then(user => {
res.send(user);
res.sendStatus(200);
})
.catch((err) => console.log(err)));
What's wrong here? Any help and advices would be greatly appreciated!
The real reason behind this problem is that you are using both
res.send(user) and res.sendStatus(200).
The error says: Error: Can't set headers after they are sent to the client
You already send something to the client (in this case res.send(user)) and you can't send another thing now (in this case res.sendStatus(200)).
Once you do res.send, res.redirect, res.sendStatus, res.render, you should not do add any of these again.
You will get this problem if you are using more than one at the same time. Even if you have something like:
for(condition) {
res.send("1st");
}
res.send("2nd");
You should add return statements in front of them like:
for(condition) {
return res.send("1st");
}
return res.send("2nd");
Also as #Halil Akar said that 200 status is always returned with res.send. You can also use res.status(301).send(user) to send data and a status at the same time.
I hope it helps.

Send an image as the body of a request, image recived with a request from outside

Yeah i kinda didn't know how to type the title well...
I've a node server which recives an image via post form. I then want to send this image to Microsoft vision and the same Google service in order to gether information from both, do some stuff, and return a result to the user that has accessed my server.
My problem is: how do i send the actual data?
This is the actual code that cares of that:
const microsofComputerVision = require("microsoft-computer-vision");
module.exports = function(req, res)
{
var file;
if(req.files)
{
file = req.files.file;
// Everything went fine
microsofComputerVision.analyzeImage(
{
"Ocp-Apim-Subscription-Key": vision_key,
"content-type": "multipart/form-data",
"body": file.data.toString(),
"visual-features":"Tags, Faces",
"request-origin":"westcentralus"
}).then((result) =>
{
console.log("A");
res.write(result);
res.end();
}).catch((err)=>
{
console.log(err);
res.writeHead(400, {'Content-Type': 'application/json'});
res.write(JSON.stringify({error: "The request must contain an image"}));
res.end();
});
}
else
{
res.writeHead(400, {'Content-Type': 'application/octet-stream'});
res.write(JSON.stringify({error: "The request must contain an image"}));
res.end();
}
}
If instead of calling "analyzeImage" i do the following
res.set('Content-Type', 'image/jpg')
res.send(file.data);
res.end();
The browser renders the image correctly, which made me think "file.data" contains the actual file (considered it's of type buffer).
But apparently Microsoft does not agree with that, because when i send the request to computer vision i get the following response:
"InvalidImageFormat"
The only examples i found are here, and the "data" that is used in that example comes from a file system read, not stright from a request. But saving the file to load it and then delete it to me looks like an horrible workaround, so i'd rather like to know in what form and how should i work on the "file" that i have to send it correctly for the APIs call.
Edit: if i use file.data (which i thought was the most correct since it would be sending the raw image as the body) i get an error which says that i must use a string or a buffer as content. So apparently that file.data is not a buffer in the way "body" requires O.o i'm not understanding honestly.
Solved, the error was quite stupid. In the "then" part, res.write(result) did not accept result as argument. This happened when i actually used the corret request (file.data which is a buffer). The other errors occurred everytime i tryed using toString() on file.data, in that case the request wasn't accepted.
Solved, the request asked for a buffer, and file.data is indeed a buffer. After chacking file.data type in any possible way i started looking for other problems. The error was much easier and, forgive my being stupid, too stupid to be evident. The result was a json, and res.write didn't accept a json as argument.
This is how I did it with Amazon Recognition Image Classifier, I know its not the same service your using - hoping this helps a little thou:
const imagePath = `./bat.jpg`;
const bitmap = fs.readFileSync(imagePath);
const params = {
Image: { Bytes: bitmap },
MaxLabels: 10,
MinConfidence: 50.0
};
route.post('/', upload.single('image'), (req, res) => {
let params = getImage();
rekognition.detectLabels(params, function(err, data) {
if (err) {
console.log('error');
}else {
console.log(data);
res.json(data);
}
});
});

Changing status code changes response body in Express

So I have a pretty simple helper function to send errors in my response. I use this function all over my codebase:
exports.error = function (err, res) {
res.send({
success: false,
errorMsg: err.message,
errors: err.errors == null ? [] : err.errors
});
};
I decided to add a status code to it:
exports.error = function (err, res, status) {
res.status(status).send({
success: false,
errorMsg: err.message,
errors: err.errors == null ? [] : err.errors
});
};
If the status is 200 I get the body exactly like the object passed to the send method. The problem is that if status is different from 200 (400 or 500 for example) my response body changes to:
{
config: Object
data: Object
headers: function (d)
status: 500
statusText: "Internal Server Error"
}
And my original response body (the one with success, errorMsg and errors fields) is inside this new response under the data attribute. I have no idea why this is happening but as far as I know I don't have any other custom error handlers in my application. I don't want this behavior and instead I want only my original response body.
I am using the body-parser package, but I believe that it only affects the requests, not the responses.
The response object that you're getting is Angular's response object (see the documentation):
The response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
AFAIK, when Angular receives a successful HTTP response (like a 200), it will run any of the default transformations to convert the response to, say, a JS object (from a JSON response).
However, it won't do that when the HTTP response indicates an error. In that case, you will get the above-mentioned response object back.
Thanks to #robertklep I found out that the problem was actually in my Angular code that handled errors. Instead of returning the response body my Angular error handler was returning the error itself.

How to get Express to return the object I just deleted in MongoDB

Feel free to let me know if this isn't a common practice - I'm a fairly new programmer - but I thought I've seen APIs in the past that, when you submit a DELETE request to a resource (/todo/1234), some servers will return the object you just deleted in the response. Is that a thing? If so, I'd be interested in learning how to do it. Here's what I have:
.delete(function (req, res) {
Todo.findById(req.params.todoId).remove(function (err) {
if (err) res.status(500).send(err);
res.send("Todo item successfully deleted");
});
});
This code does delete the item, but I would like to return the item that got deleted in the response instead of a string message. If that's a normal/okay thing to do. If it isn't normal or okay for some reason, please let me know why and I'll just move on. Or perhaps there's a more common way.
This is what I found in the [RFC 7231 docs][1]:
If a DELETE method is successfully applied, the origin server SHOULD
send a 202 (Accepted) status code if the action will likely succeed
but has not yet been enacted, a 204 (No Content) status code if the
action has been enacted and no further information is to be supplied,
or a 200 (OK) status code if the action has been enacted and the
response message includes a representation describing the status.
I'm having a hard time interpreting what the 200 response means - is it only kosher to send a string message (Success!) or an object containing a message attribute ({message: "Success!"})? Or can you do whatever you want there? What's the best practice in Express using Mongoose?
Thanks in advance for the help, and sorry for my noobness with HTTP stuff.
[1]: https://www.rfc-editor.org/rfc/rfc7231#section-4.3.5
You should use findOneAndRemove! Something like:
Todo.findOneAndremove({ id: req.params.todoId }, function( error, doc, result) {
// it will be already removed, but doc is what you need:
if (err) res.status(500).send(err);
res.send(doc.id);
});

Resources