I have an Express.JS server with many res.json. To perform statistics, logging and debugging, I want to capture the response payload in a catch-all hook.
I have found the finish event res.on('finish') but I cannot find how to extract the response payload from it.
res.on('finish') is called once the data has been sent, so the payload doesn't necessarily exist on the server any more. You can add middleware to express to intercept every request and then override the .json method to log the data as the function is called:
router.use('/', (req, res, next) => {
var old = res.json.bind(res);
res.json = (body) => {
//Do whatever
old(body);
}
next();
})
Related
I don't know why but i try to send an object in my back. I can find good information in my network's payload but my req.body return everytime an empty object
my probleme
add app.use(express.json());
express.json() is a middleware that allows express to recognise the json data in request object.
Also your controller is not sending any response back, for that use:
app.post("/", (req, res) => {
res.status(200).json({"data" : req.body});
}
I am creating a MERN app that adds meta tags to React pages without SSR. So, I need to read the query inside the main file of the server and pass the appropriate metadata content to each page.
I am using this in the server.js file:
const indexPath = path.resolve(__dirname, 'build', 'index.html');
// static resources should just be served as they are
app.use(express.static(
path.resolve(__dirname, 'build'),
{ maxAge: '30d' },
));
// here we serve the index.html page
app.get('/*', (req, res, next) => {
fs.readFile(indexPath, 'utf8', (err, htmlData) => {
if (err) {
console.error('Error during file reading', err);
return res.status(404).end()
}
// get post info
const postId = req.query.id;
const post = getPostById(postId);
if(!post) return res.status(404).send("Post not found");
// inject meta tags
htmlData = htmlData.replace(
"<title>React App</title>",
`<title>${post.title}</title>`
)
.replace('__META_OG_TITLE__', post.title)
.replace('__META_OG_DESCRIPTION__', post.description)
.replace('__META_DESCRIPTION__', post.description)
.replace('__META_OG_IMAGE__', post.thumbnail)
return res.send(htmlData);
});
});
Here the getPostById is statically defined in a file. But I want to fetch it from my db.
My file structure is:
server.js
controllers
- posts.js
routes
- posts.js
I've separated the logic from route. So my routes/posts.js file looks like:
import { getPost, createPost } from '../controllers/posts.js';
const router = express.Router();
router.get('/', getPost);
router.post('/', createPost);
export default router;
So, in order to dynamically pass the meta content, I need to read the API endpoint for each request and pass the appropriate data. For this, I need to call the endpoints directly inside my node project. How to do that?
I'd appreciate any help. Thank you.
If you really want to call your own http endpoints, you would use http.get() or some higher level http library (that is a little easier to use) such as got(). And, then you can make an http request to your own server and get the results back.
But ... usually, you do not make http requests to your own server. Instead, you encapsulate the functionality that gets you the data you want in a function and you use that function both in the route and in your own code that wants the same data as the route. This is a ton more efficient than packaging up an http request, sending that request to the TCP stack, having that request come back to your server, parsing that request, getting the data, forming it as an http response, sending that response back to the requester, parsing that response, then using the data.
Instead, if you have a common, shared function, you just call the function, get the result from it (probably via a promise) and you're done. You don't need all that intermediate packaging into the http request/response, parsing, loopback network, etc...
I made a web server that serves as a client-side using socket.io-client and express (because I have to use this form in other project).
It emits the string posted and when receiving 'boom' emit from io server it responds by sending the string served.
Posting 'heat_bomb' works well for the first time, but when I try second time '[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client' occurs at res.send(data) in socket.on().
Is there a way to refresh whenever post request is generated, so that each request uses independent response?
app.ts
import express from 'express'
import {io} from 'socket.io-client'
import bodyParser from 'body-parser'
const app=express()
const PORT=8080
const socket=io(`http://localhost:2002`, {
query:{
hello:"merhaba"
}
})
app.use(bodyParser.urlencoded({extended:false}))
app.get('/', (req, res)=>{
res.sendFile(__dirname+`/index.html`)
})
app.post('/heat_bomb', (req, res)=>{
socket.emit('heat_bomb', req.body.elem)
socket.on('boom', (data)=>{
res.send(data)
})
})
app.listen(PORT, ()=>{
console.log(`Server Running: ${PORT}`)
})
index.html
$('#heat_button').click(function(){
console.log('heating bomb')
$.post('/heat_bomb', {elem: $('#input_number').val()},(data, status)=>{
console.log(data)
console.log('heated')
})
})
Your /heat_bomb middleware registers a new boom handler for every request on the same globally defined socket. Although your code snippet does not show how the heat_bomb and boom events are connected, I assume that the emit('boom') during the second request re-triggers the heat_bomb handler that was registered during the first request, leading to another res.send for the res from the first request, which is already completed. This leads to the observed error message.
socket.once('boom') alone will not solve this reliably: if one heat_bomb event can overtake another, the data from the first can wrongly be "paired" with the res from the second. The res needs to be a second argument of your events, something like this:
socket.emit('heat_bomb', req.body.elem, res);
socket.once('boom', function(data, res) {
res.send(data);
});
I cant access body when getting expressjs request
My main server.js file
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(router);
My router.js file
const express = require("express");
const articlesContr = require("./../controllers/articlesContr.js");
const router = express.Router();
router
.get("/articles", articlesContr.handleGet)
.post("/articles", articlesContr.handlePost)
.put("/articles", articlesContr.handleUpdate)
.delete("/articles", articlesContr.handleDelete);
adminPanelContr.js
const controller = {
handleGet: (req, res) => {
const query = req._parsedUrl.query;
const parsedQuery = querystring.parse(query);
// res.send(productsDb.getItem(parsedQuery));
console.log(req)
res.send(JSON.stringify(req.body))
}
};
GET requests don't have body parameters. Ordinarily .get() handler functions don't look for them.
If you use the same handler function for GET and other (POST, PUT, PATCH, DELETE) requests, you need logic to avoid looking at the body when you get a GET.
When you send a request with a JSON body, don't forget to tell your server it's JSON by including a Content-Type: application/json HTTP header. That way you're sure express will invoke its express.json middleware to parse the body into the req.body object.
When you .get() a GET, you don't get a request body. There's gotta be some sort of doggerel poetry in there someplace.
If you use res.json(object) rather than res.send(JSON.stringify(object)) express does a good job of setting the HTTP response headers to useful values for JSON.
And, I'm sure you know this: Responding to a request with its own request body is a little strange.
I am a relative newbie of Node.js. It been two days that I am trying to modify the body of a Request in Node.js and then forwarding it. For proxying I am using http-proxy module.
What I have to do is to intercept the password of a user inside a JSON object, encrypting it and set the new encrypted password inside the request body.
The problem is that every time I try to collect the request body I consume it (i.e. using body-parser). How can I accomplish this task? I know that the Request in node is seen has a stream.
For sake o completeness, I am using express to chain multiple operation before proxying.
EDIT
The fact that I have to proxy the request is not useless. It follows the code that I am trying to use.
function encipher(req, res, next){
var password = req.body.password;
var encryptionData = Crypto().saltHashPassword(password);
req.body.password = encryptionData.passwordHash;
req.body['salt'] = encryptionData.salt;
next();
}
server.post("/users", bodyParser.json(), encipher, function(req, res) {
apiProxy.web(req, res, {target: apiUserForwardingUrl});
});
The server (REST made by Spring MVC) give me the exception Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: null
The real problem is that there is an integration problem between modules body-parser and http-proxy, as stated in this thread.
One solution is to configure body-parser after http-proxy. If you can't change the order of the middleware (as in my case), you can restream the parsed body before proxying the request.
// restream parsed body before proxying
proxy.on('proxyReq', function(proxyReq, req, res, options) {
if (req.body) {
let bodyData = JSON.stringify(req.body);
// if content-type is application/x-www-form-urlencoded -> we need to change to application/json
proxyReq.setHeader('Content-Type','application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
// stream the content
proxyReq.write(bodyData);
}
}
Why don't use express chaining for this ?
In your first function just do something like this :
req.body.password = encrypt(req.body.password); next();
You just have to use a middleware.
body-parser is also just a middleware that parses the request bodies and puts it under req.body
You can do something like this:
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
function encryptPassword(req, res, next) {
req.body.password = encrypt(req.body.password);
// You can do anything really here and modify the req
//call next after you are done to pass on to next function
next();
}
app.use(encryptPassword);
Generally people use middlewares for authentication, role-based access control etc.....
You can use middlewares in particular routes also:
app.post('/password', encryptPassword, function(req, res) {
// Here the req.body.password is the encrypted password....
// You can do other operations related to this endpoint, like store password in database
return res.status(201).send("Password updated!!");
});