MST

星途 面试题库

面试题:Node.js Express框架下的错误处理机制与性能优化

在使用Node.js的Express框架进行项目开发时,如何优雅地处理不同类型的错误(如路由错误、运行时错误等)?针对Express应用,你可以从哪些方面进行性能优化,例如中间件的使用、路由的设计等,详细说明你的思路。
14.3万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

错误处理

  1. 路由错误处理
    • 使用next函数:在路由处理函数中,如果遇到不符合预期的请求(例如请求的参数格式不正确等),可以调用next函数并传入一个错误对象。例如:
    const express = require('express');
    const app = express();
    
    app.get('/user/:id', (req, res, next) => {
        const id = parseInt(req.params.id);
        if (isNaN(id)) {
            const err = new Error('Invalid user ID');
            err.status = 400;
            next(err);
        } else {
            // 正常处理逻辑
            res.send(`User ID: ${id}`);
        }
    });
    
    • 全局错误处理中间件:定义一个全局错误处理中间件,放在所有路由和其他中间件之后。这个中间件接收四个参数:err(错误对象)、req(请求对象)、res(响应对象)和nextnext函数,虽然在这里通常不使用)。
    app.use((err, req, res, next) => {
        console.error(err.stack);
        if (!err.status) {
            err.status = 500;
        }
        res.status(err.status).send(err.message);
    });
    
  2. 运行时错误处理
    • 使用try - catch:在可能抛出运行时错误(如数据库查询失败、文件读取错误等)的异步操作中,使用try - catch块捕获错误并通过next传递给全局错误处理中间件。例如,使用async/await进行数据库查询:
    const { Pool } = require('pg');
    const pool = new Pool({
        user: 'user',
        host: 'host',
        database: 'database',
        password: 'password',
        port: 5432,
    });
    
    app.get('/data', async (req, res, next) => {
        try {
            const result = await pool.query('SELECT * FROM some_table');
            res.send(result.rows);
        } catch (err) {
            next(err);
        }
    });
    
    • Promise 链中的错误处理:如果使用Promise进行异步操作,可以在Promise链的末尾使用.catch方法捕获错误并传递给next。例如:
    app.get('/data', (req, res, next) => {
        pool.query('SELECT * FROM some_table')
           .then(result => {
                res.send(result.rows);
            })
           .catch(err => {
                next(err);
            });
    });
    

性能优化

  1. 中间件的使用
    • 精简中间件数量:只使用必要的中间件,避免引入过多不必要的功能。例如,如果应用不需要处理静态文件,就不要引入express - static中间件。
    • 合理排序中间件:中间件的执行顺序很重要。例如,body - parser中间件应该放在路由定义之前,以便在路由处理函数中能够正确获取请求体数据。安全相关的中间件(如helmet)也应该尽早执行。
    • 使用高效的中间件:对于常见的功能,选择性能较好的中间件。例如,对于日志记录,morgan是一个轻量级且高效的日志中间件。
  2. 路由的设计
    • 路由分层:按照功能模块对路由进行分层,例如将用户相关的路由放在/users前缀下,订单相关的路由放在/orders前缀下。这样可以提高代码的可维护性,并且在处理大量路由时,有助于优化路由匹配性能。
    • 减少路由嵌套深度:过深的路由嵌套可能导致路由匹配性能下降。尽量保持路由结构扁平,例如/users/:id/orders/users/:id/settings/orders更优。
    • 使用路由参数缓存:对于频繁访问且参数不变的路由,可以考虑缓存响应结果。例如,使用express - cache - response中间件,根据路由参数和请求方法缓存响应。
  3. 其他方面
    • 启用gzip压缩:使用compression中间件对响应数据进行gzip压缩,减少网络传输的数据量,提高响应速度。
    const compression = require('compression');
    app.use(compression());
    
    • 优化数据库查询:在Express应用与数据库交互时,确保数据库查询是高效的。使用合适的索引,避免全表扫描,并且合理控制数据库连接池的大小,以避免资源浪费。
    • 使用CDN:对于静态资源(如CSS、JavaScript、图片等),使用CDN(内容分发网络)来加速资源的加载。可以将静态资源上传到CDN服务提供商,然后在HTML中引用CDN链接。