difference between res.send or res.json - node.js

What is the difference between
res.status(STATUS_CODE).send({"message" : "this is the message" });
and
res.status(STATUS_CODE).json({"message" : "this is the message" });
although checked the similar question but that is in context of express 3 and I am looking for express 4

Ultimately, both will achieve the same thing. If you call res.send with an object, it will hit this switch in res.send:
switch (typeof chunk) {
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
this.type('html');
}
break;
case 'boolean':
case 'number':
case 'object':
if (chunk === null) {
chunk = '';
} else if (Buffer.isBuffer(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
} else {
return this.json(chunk);
}
break;
}
If the object you're sending it is not a Buffer - it will call res.json.
res.json simply sets the Content-Type header to application/json and runs the object through JSON.stringify - with specified replacer function and spacer value. Eventually it calls res.send.
This call of res.send is sent a string and the case statement will break resulting in the rest of the function being run. The remainder of the send function sets things like etag, content size etc. You can find out more by looking at the express code.
They start to differ when you send them non-object responses, such as strings, numbers etc. As in that case res.json will run it through JSON.stringify but res.send wont: resulting in different Content-Type headers and content.
edit: to answer your comment on the other answer, sending different status codes will still behave the same.

.json calls .toJSON() method on the object (if it exists) and also sets the content-type header to application/json
But basically you can simulate .json with .send, if you do the above manually.
For example this middleware
(req, res, next) => {
let x = {};
x.toJSON = () => {
console.log('oh yeah');
return 15;
};
return res.json(x);
}
Prints
oh yeah
And returns 15 to the request with these headers (at least in my simple express app):
Connection →keep-alive
Content-Length →2
Content-Type →application/json; charset=utf-8
Date →Wed, 08 Jun 2016 10:16:09 GMT
ETag →W/"2-m/Mcf/Bik2qW08i9H48v8w"
Vary →Accept-Encoding
X-Powered-By →Express

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.

express js set request header Content-Type in middleware based on file extension?

The end points of my API-application render data based on the request mime type. I have implementing express' response.format() to distinguish between the req types.
However, the handler also needs to be able to distinguish the formats based on the file extension like
/route/users.json
Express' response.format doesn't handle that case.
Therefore I was hoping I could simply use a middleware function and set the headers Content-Type according to the extension like this:
app.use('/', (req, res, next) => {
let contentType;
if (req.path.match(/\.json/)) {
contentType = 'application/json';
} else if (req.path.match(/\.xml/)) {
contentType = 'text/xml';
}
if (contentType) {
req.headers['Content-Type'] = contentType;
}
next();
});
But that's not working. I have checked whether the middleware is actually executed and that's the case. However the Content-Type does not persist.
So when the actual request handler is being executed the Content-Type is missing.
What am I doing wrong here?
So, the non-persistent content content type wasn't the problem. In fact the content-type was there, but due to focussing on the wrong problem, I got quite confused.
Anyway, the real "problem" was, that the header mime type was not available in the request header's accept field.
If that's not set express and any other sane web server won't recognize it.
Here's what I neede to do:
module.exports = function fileExtensionHeader(req, res, next) {
let contentType;
if (req.path.match(/\.json/)) {
contentType = 'application/json';
} else if (req.path.match(/\.xml/)) {
contentType = 'text/xml';
}
if (contentType) {
req.headers['Content-Type'] = contentType;
req.headers.accept = `${contentType},${req.headers.accept}`; // this line did the trick
}
next();
};
A word of warning:
This creates a potential security risk. The mime type should only be set by the request header sent by the browser.
No one should ever allow to set the mime type by file extension without a good reason.

Axios get response headers. Works in node, not in react

I will start off by saying this has nothing to do with authentication of JWT tokens.
I am trying to pull data from a public api. Lets call it www.abc.com/WeatherAPI.
When I do this in node with axios using axios.get(url), I am able to console.log(res.headers) and they show perfectly.( I need them for pagination and a recursive function)
When I use the EXACT SAME code in react, I get empty headers returned....
eg: content-type: "application/json"
date: "Sun, 08 Mar 2020 09:23:03 GMT"
Code:
return axios
.get(
'https://api.xxxxxxxx' +
(cursor ? '&cursor=' + cursor : '')
)
.then(res => {
console.log(res);
console.log(res.headers['cursor']);
// If there is no data, return...
if (res.data.length < 1) return;
// Push current data into OB state
setOB({ ...oB, ...res.data });
//te
//If there is no cursor... ie there is no more data, return
if (!res.headers['cursor']) return;
return OB(res.headers['cursor']);
});
};
// I dont know if use effect is right here... we will see..
useEffect(() => {
OB();
}, []);`
as the API is public, it could be that the response header differs based on the agent. it is not likely to be the case but it can be. I would suggest overriding the headers object including the User-Agent
axios.get(
`https://api.xxxxxxxx${cursor ? '&cursor=' + cursor : ''}`,
{ headers: { 'User-Agent': 'YOUR-SERVICE-NAME' } },
).then()...

Return JSON response in ISO-8859-1 with NodeJS/Express

I have built an API with Node and Express which returns some JSON. The JSON data is to be read by a web application. Sadly this application only accepts ISO-8859-1 encoded JSON which has proven to be a bit difficult.
I can't manage to return the JSON with the right encoding even though I've tried the methods in the Express documentation and also all tips from googling the issue.
The Express documentation says to use "res.set()" or "res.type()" but none of these is working for me. The commented lines are all the variants that I've tried (using Mongoose):
MyModel.find()
.sort([['name', 'ascending']])
.exec((err, result) => {
if (err) { return next(err) }
// res.set('Content-Type', 'application/json; charset=iso-8859-1')
// res.set('Content-Type', 'application/json; charset=ansi')
// res.set('Content-Type', 'application/json; charset=windows-1252')
// res.type('application/json; charset=iso-8859-1')
// res.type('application/json; charset=ansi')
// res.type('application/json; charset=windows-1252')
// res.send(result)
res.json(result)
})
None of these have any effect on the response, it always turns into "Content-Type: application/json; charset=utf-8".
Since JSON should(?) be encoded in utf-8, is it event possible to use any other encoding with Express?
If you look at the lib/response.js file in the Express source code (in your node_modules folder or at https://github.com/expressjs/express/blob/master/lib/response.js) you'll see that res.json takes your result, generates the corresponding JSON representation in a JavaScript String, and then passes that string to res.send.
The cause of your problem is that when res.send (in that same source file) is given a String argument, it encodes the string as UTF8 and also forces the charset of the response to utf-8.
You can get around this by not using res.json. Instead build the encoded response yourself. First use your existing code to set up the Content-Type header:
res.set('Content-Type', 'application/json; charset=iso-8859-1')
After that, manually generate the JSON string:
jsonString = JSON.stringify(result);
then encode that string as ISO-8859-1 into a Buffer:
jsonBuffer = Buffer.from(jsonString, 'latin1');
Finally, pass that buffer to res.send:
res.send(jsonBuffer)
Because res.send is no longer being called with a String argument, it should skip the step where it forces charset=utf-8 and should send the response with the charset value that you specified.

Setting content-type header with restify results in application/octet-stream

I'm trying out restify, and though I'm more comfortable with Express, so far it's pretty awesome. I'm trying to set the content type header in the response like so:
server.get('/xml', function(req, res) {
res.setHeader('content-type', 'application/xml');
// res.header('content-type', 'application/xml'); // tried this too
// res.contentType = "application/xml"; // tried this too
res.send("<root><test>stuff</test></root>");
});
But the response I get back is instead application/octet-stream.
I also tried res.contentType('application/xml') but that actually threw an error ("Object HTTP/1.1 200 OK\ has no method 'contentType'").
What is the correct way to set the content type header to xml on the response?
Update:
When I do console.log(res.contentType); it actually outputs application/xml. Why is it not in the response headers?
Curl snippet:
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /xml?params=1,2,3 HTTP/1.1
> User-Agent: curl/7.39.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
< Content-Length: 8995
< Date: Mon, 23 Feb 2015 20:20:14 GMT
< Connection: keep-alive
<
<body goes here>
Turns out the reason this was failing is because I was not sending the response using Restify's response handler; it was defaulting to the native Node.js handler.
Where I was doing this:
res.send(js2xmlparser("search", obj));
I should have been doing this:
res.end(js2xmlparser("search", o));
// ^ end, not send!
When I do console.log(res.contentType); it actually outputs application/xml. Why is it not in the response headers?
All you've done there is set a property on the res object. And because this is JavaScript, that works fine and you can read the property value back, but that's not the correct API for either node core or restify, so it is ignored by everything other than your code.
Your res.header("Content-Type", "application/xml"); looks correct to me based on the restify docs you linked to. Therefore my hunch is your tooling may be misleading you. Are you sure you are seeing the raw values in the response (many developer tools will unhelpfully "prettify" or otherwise lie to you) and you are hitting the route you really think you are? Output of curl -v or httpie --headers would be helpful.
It is possible to return application/xml by adding a formatter to the server instance at server creation:
var server = restify.createServer( {
formatters: {
'application/xml' : function( req, res, body, cb ) {
if (body instanceof Error)
return body.stack;
if (Buffer.isBuffer(body))
return cb(null, body.toString('base64'));
return cb(null, body);
}
}
});
Then at some part of the code:
res.setHeader('content-type', 'application/xml');
res.send('<xml>xyz</xml>');
Please, take a look at: http://restify.com/#content-negotiation
You can send the XML response using sendRaw instead of send. The sendRaw method doesn't use any formatter at all (you should preformat your response if you need it). See an example below:
server.get('/xml', function(req, res, next) {
res.setHeader('content-type', 'application/xml');
res.sendRaw('<xml>xyz</xml>');
next();
});

Resources