Custom middleware express.js framework ordering

℡╲_俬逩灬. 提交于 2021-01-28 22:04:12

问题


I am writing custom middleware(s) to send request data(like request path, response code, request timestamp, etc) to a remote server on every request. I'm unable to figure out the ordering of my middlewares.

I have created 2 middlewares,
a) process_request (m1) --> this middleware just adds a timestamp to the request object to register when the request was received.
b) process_response (m2) --> this middleware posts the required data to the remote server

m1

function process_request(req, res, next) {
  req.requestTime = Date.now()/1000;
  next();
}

m2

function process_response(req, res, next) {
  const request_obj = {};
  request_obj.request_timestamp = req.requestTime;
  request_obj.response_timestamp = Date.now()/1000;
  request_obj.path = req.protocol + "://" + 
    req.get('host') + req.originalUrl;
  request_obj.response_code = res.statusCode;
  send_perf_request(request_obj); // sends remote https request not shown here
}

I can think of two ordering options in my app.js:
Order 1:

m1     
route 1   
route 2    
...
route n      
m2         
404 request handler middleware

Order 2:

m1     
route 1   
route 2    
...
route n              
404 request handler middleware
m2

404 request handler middleware

app.use((req, res, next) => {
  const err = new Error('Not Found');
  err.status = 404;
  next(err);
});

The problem with order 1 is that I won't be able to catch 404 requests, which I want to.
The problem with order 2 in all in request the responseCode=404 as the 404 request handler middleware is doing that.
I am new to node.js & confused if I am thinking about this the right way.

My app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

const custom_middleware = require("custom_middleware");


var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use(custom_middleware.process_request);  //*calling middleware


app.use('/', indexRouter);
app.use('/users', usersRouter);

//catch 404 and forward to error handler
// app.use(function(req, res, next) {
//   console.log("in 404 handler");
//   //next(createError(404, "this page doesn't exist;"));
//   next();
// });


// error handler
app.use(function(err, req, res, next) {
  // this is called only in case of errors  
  // set locals, only providing error in development
  console.log("in error handler");
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  console.log(res.statusCode);
  //res.render('error');
  next(err);
});

app.use(custom_middleware.process_exception); //*calling middleware

module.exports = app;

Custom middleware file

function process_request(req, res, next) {
  // this middleware adds request timestamp
  console.log("in process request middleware");
  req.requestTime = Date.now()/1000;
  res.on("finish", function (){
        // this middleware collects performance data
        console.log("in process response middleware");
        const request_obj = {};
        request_obj.request_timestamp = req.requestTime;
        request_obj.response_timestamp = Date.now()/1000;
        request_obj.ip_address = ip.address();
        request_obj.path = req.protocol + "://" + 
        req.get('host') + req.originalUrl;
        request_obj.requester = null;
        request_obj.response_code = res.statusCode;
        console.log(request_obj.response_code);
        send_perf_request(request_obj);
    })
  next();
}

function process_exception(err, req, res, next) {
  // this middleware collects exception data
  console.log("in process exception middleware");
  const error_obj = {};
  error_obj.path = req.protocol + "://" + 
    req.hostname + req.originalUrl;
  error_obj.exception = "error occured";
  error_obj.traceback = err.stack;
  error_obj.user = null;
  error_obj.ip_address = ip.address();
  send_exception_request(error_obj);
  next();
}

My routes/index.js

var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  throw new Error('this does not exist'); // error manually triggered
  res.status(500);
});

module.exports = router;

回答1:


As mentioned in the comments, abstract the middlewares to avoid defining a set of middlewares on each route.

To replace m1 Create a global middleware, which you define before all your other middlewares which sets res.on("finish", function () {}) event handler to do something when the route is done. I'm pretty sure this is the only event you will get the actual correct res.statusCode if you're doing res.status() anywhere.

Then move your 404 error handler logic into the main error handler, you can then check for status code and all that good stuff before logging and responding. You can also use req.xhr to determine how to respond.

Also, you can use a catch-all to pick up on route 404's: app.get('*', function (req, res, next) { then fire off an error which the error handler handles.

Here is an example putting it all together:

const express = require('express')
const app = express()

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

  // set something
  req.requestTime = Date.now() / 1000;

  // gets fired after all routes have been handled
  res.on("finish", function () {

    //
    req.finishTime = Date.now() / 1000;

    // do something with req, res objects
    console.log('[in logger] originalUrl:', req.originalUrl)
    console.log('[in logger] status code:', res.statusCode)
  })

  next()
})

// apply routes
// ...

app.get('/', (req, res, next) => {
  try {
    // example: look for something, which is not found
    const mock = false
    if (!mock) {
      let err = new Error('Mock was not found!')
      err.status = 404
      throw err
    }

    res.send('Hello World!')
  } catch (err) {
    next(err)
  }
})

// app.get('/foo', ...

// not found route (catch all)
app.get('*', (req, res, next) => {
  let err = new Error('Page not found!')
  err.status = 404
  next(err)
})

// error handler (will catch everything even uncaught exceptions)
app.use((error, req, res, next) => {

  //
  res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
  res.header('Expires', '-1')
  res.header('Pragma', 'no-cache')

  // set status from error
  res.status(error.status || 500)

  if (req.xhr) {
    // is ajax call, send error as json
    res.json({
      error: error.name || 'Server error',
      status: error.status || 500,
      message: error.message || 'Internal server error'
    })
  } else {
    // render a view instead (would need to add a view engine)
    res.render('error/' + (error.status || 500), error)
  }
})

app.listen(3000, function () {
  console.log('App listening on port 3000!')
})


来源:https://stackoverflow.com/questions/64500279/custom-middleware-express-js-framework-ordering

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!