You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

核心Node.js中Express的app.use()等价方案及无框架下用connect-mongo实现会话

Hey there! Since you're just getting started with raw Node.js backend development (no frameworks like Express), let's tackle your two questions clearly and practically.

1. Implementing Express-like app.use() in Raw Node.js

First, let's recall what Express's app.use() does: it lets you mount middleware functions that run for every (or path-matched) incoming HTTP request, in the order you define them. Each middleware gets access to the request (req), response (res), and a next() function to pass control to the next middleware.

To replicate this in raw Node.js, we can create a simple wrapper around the core http module that maintains a list of middleware functions and executes them sequentially for each request. Here's a working example:

const http = require('http');

// Our custom "app" object to mimic Express's middleware pattern
const app = {
  middleware: [],
  // Add middleware to our list (supports optional path matching)
  use(pathOrFn, fn) {
    if (typeof pathOrFn === 'function') {
      // If only a function is passed, default to matching all paths ('/')
      this.middleware.push({ path: '/', handler: pathOrFn });
    } else {
      this.middleware.push({ path: pathOrFn, handler: fn });
    }
  },
  // Start the server and handle requests
  listen(port, callback) {
    const server = http.createServer((req, res) => {
      let middlewareIndex = 0;

      // The next() function to pass control to the next middleware
      function next() {
        if (middlewareIndex >= app.middleware.length) {
          // All middleware executed; send a default 404 if no response was sent
          if (!res.headersSent) {
            res.writeHead(404, { 'Content-Type': 'text/plain' });
            res.end('404 - Not Found');
          }
          return;
        }

        const { path, handler } = app.middleware[middlewareIndex++];
        // Simple path matching: check if the request URL starts with the middleware's path
        if (req.url.startsWith(path)) {
          handler(req, res, next);
        } else {
          // Skip this middleware if the path doesn't match
          next();
        }
      }

      // Kick off the middleware chain
      next();
    });

    server.listen(port, callback);
  }
};

// Example usage of our custom app.use()
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
});

app.use('/hello', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello from our custom middleware!');
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Key Details:

  • The use() method adds middleware to an array, supporting both path-specific and global middleware.
  • The listen() method creates a Node.js HTTP server, and for each request, it runs through the middleware chain using the next() function.
  • We include basic path matching to mimic Express's behavior, but you can expand this (e.g., support wildcards) if needed.
2. Using connect-mongo for Sessions in Raw Node.js

connect-mongo is built to work with Express's express-session middleware, but we can adapt it for raw Node.js by manually handling session logic: parsing cookies, generating session IDs, and using connect-mongo as the session storage layer.

Step 1: Install Dependencies

First, install the required packages:

npm install connect-mongo cookie-parser
  • cookie-parser simplifies parsing request cookies (you could do this manually, but it saves time).
  • crypto is a core Node.js module we'll use for generating secure session IDs.

Step 2: Working Implementation

const http = require('http');
const { MongoStore } = require('connect-mongo');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');

// Initialize the MongoDB session store
const sessionStore = new MongoStore({
  mongoUrl: 'mongodb://localhost:27017/your-db-name', // Replace with your MongoDB URI
  collectionName: 'sessions',
  ttl: 86400 // Session expires after 24 hours (in seconds)
});

// Custom session middleware to mimic express-session
function sessionMiddleware(req, res, next) {
  // First, parse the request cookies
  cookieParser()(req, res, () => {
    // Get the session ID from the cookie (matches express-session's default cookie name)
    let sessionId = req.cookies['connect.sid'];

    // If no session ID exists, generate a new secure one
    if (!sessionId) {
      sessionId = crypto.randomBytes(32).toString('hex');
      // Set the session cookie (HttpOnly prevents XSS attacks)
      res.setHeader('Set-Cookie', `connect.sid=${sessionId}; HttpOnly; Path=/; Max-Age=${86400}`);
    }

    // Fetch the session data from MongoDB
    sessionStore.get(sessionId, (err, sessionData) => {
      if (err) return next(err);

      // Attach session data to the request object (like Express does)
      req.session = sessionData || {};

      // Override res.end to save session data when the response finishes
      const originalEnd = res.end;
      res.end = function(data, encoding) {
        sessionStore.set(sessionId, req.session, (saveErr) => {
          if (saveErr) console.error('Failed to save session:', saveErr);
          originalEnd.call(this, data, encoding);
        });
      };

      // Move to the next middleware/request handler
      next();
    });
  });
}

// Create our server and use the session middleware
const server = http.createServer((req, res) => {
  sessionMiddleware(req, res, () => {
    // Handle different routes
    if (req.url === '/') {
      // Track page views with the session
      req.session.views = (req.session.views || 0) + 1;
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.end(`You've visited this page ${req.session.views} times!`);
    } else if (req.url === '/logout') {
      // Destroy the session
      sessionStore.destroy(sessionId, (err) => {
        if (err) console.error('Failed to destroy session:', err);
        // Clear the session cookie
        res.setHeader('Set-Cookie', 'connect.sid=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT');
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Logged out successfully!');
      });
    } else {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('404 - Not Found');
    }
  });
});

server.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Key Details:

  • We initialize MongoStore with your MongoDB connection string and session TTL (time-to-live) for automatic session cleanup.
  • The sessionMiddleware handles cookie parsing, session ID generation, fetching/saving session data to MongoDB.
  • We override res.end to ensure session data is saved when the response is sent, just like Express's session middleware does.
  • Make sure your MongoDB server is running before starting the Node.js server!

内容的提问来源于stack exchange,提问作者Hakimuddin

火山引擎 最新活动