简体   繁体   中英

How to get response body in middleware?

I want to log every request with response information.

I tried to use middleware, and I have a problem.

res.body is undefined.

app.use((req, res, next) => {

    var time = Date.now();

    res.on('finish', function() {
        var clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
        var method = req.method;
        var path = req.baseUrl;
        var duration = Date.now() - time;

        console.log({
            clientIp,
            elapsedTime: `${duration}ms`,
            hostname: req.headers.host,
            level: 'INFO',
            method,
            path,
            phase: process.env.NODE_ENV,
            reqBody: req.body,
            reqHeaders: req.headers,
            resBody: res.body,
            resHeaders: res.getHeaders(),
            status: res.statusCode
        });
    });

    next();
});

Actually there is response data on client.

How can I get response body in middleware?

The response can be intercepted by overriding response.json function. By doing so, and adding our custom function , every time, response.json() is called, our intercepting function is triggered.

middleware/response.filter.js:

// Response Interceptor Middleware
export default (request, response, next) => {
    try {
        const oldJSON = response.json;
        response.json = (data) => {
            // For Async call, handle the promise and then set the data to `oldJson`
            if (data && data.then != undefined) {
                // Resetting json to original to avoid cyclic call.
                return data.then((responseData) => {
                    // Custom logic/code.
                    response.json = oldJSON;
                    return oldJSON.call(response, responseData);
                }).catch((error) => {
                    next(error);
                });
            } else {
                // For non-async interceptor functions
                // Resetting json to original to avoid cyclic call.
                // Custom logic/code.
                response.json = oldJSON;
                return oldJSON.call(response, finalResponse);
            }
        }
    } catch (error) {
        next(error);
    }
}

In the Server.js file, register the middleware:

// Server.js file
import externalResponseFilter from "./middleware/response.filter.js:";

// Create Express server
const app = express();

// Response interceptor - Initialization.
app.use(externalResponseFilter);

And in the controller where you are returning the response , return with response.json() function instead of response.send() .

Let me know if any additional explanation is required.

The response object in Express is simply node's http.ServerResponse class. It is a writable Stream so you can interact with it as a stream. It also exposes the underlying net.Socket connection via res.socket .

This is where it gets interesting. Because net.Socket is a plain writable Stream. If you override it's .write() method you will be able to intercept all bytes going out of your socket onto the network:

function interceptorMiddleware (req, res, next) {
    const sock  = req.socket;
    const write = sock.write.bind(sock);

    sock.write = (data, encoding, callback) => {
        console.log(data);
        write(data, encoding, callback);
    }

    sock.on('close', () => console.log('DONE'));

    next();
}

You need to install this middleware very early in your application before anything gets a chance to write to the socket.

Due to the asynchronous nature of node you may get mixed packets in your log output so it may be worth adding a bit of code to identify which socket is generating an output:

console.log(sock.remoteAddress, sock.remotePort, data);

Warning!

console.log() is synchronous when outputting to the terminal or a file. Doing this can potentially severely degrade your server's performance. If you are piping stdout to another process such as pm2 then it's not so bad because it will be async but it may still cost you a minor performance hit.

Use this for debugging ONLY

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM