MST

星途 面试题库

面试题:Node.js Express复杂路由参数与查询字符串场景优化

在一个复杂的Node.js Express应用中,有多层嵌套路由如 '/category/:categoryId/subcategory/:subcategoryId/product/:productId',同时查询字符串可能包含多个过滤条件如 '?color=red&size=large'。在高并发场景下,如何高效地解析这些参数和查询字符串,并确保数据处理的准确性和性能?请从架构设计、缓存策略以及异步处理等方面阐述你的思路,并给出关键代码示例。
39.7万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

架构设计

  1. 模块化路由:将不同层级的路由分开定义,便于维护和管理。例如:
const express = require('express');
const categoryRouter = express.Router();
const subcategoryRouter = express.Router();
const productRouter = express.Router();

categoryRouter.param('categoryId', (req, res, next, categoryId) => {
    // 这里可以对categoryId进行验证等预处理
    req.categoryId = categoryId;
    next();
});
subcategoryRouter.param('subcategoryId', (req, res, next, subcategoryId) => {
    req.subcategoryId = subcategoryId;
    next();
});
productRouter.param('productId', (req, res, next, productId) => {
    req.productId = productId;
    next();
});

productRouter.get('/', (req, res) => {
    // 处理/product/:productId请求
});
subcategoryRouter.use('/product', productRouter);
categoryRouter.use('/subcategory/:subcategoryId', subcategoryRouter);
app.use('/category/:categoryId', categoryRouter);
  1. 中间件分层:使用中间件对请求进行预处理,如参数验证、日志记录等。
const validateQuery = (req, res, next) => {
    const { color, size } = req.query;
    if (color && typeof color!=='string') {
        return res.status(400).send('Invalid color query parameter');
    }
    if (size && typeof size!=='string') {
        return res.status(400).send('Invalid size query parameter');
    }
    next();
};
app.get('/category/:categoryId/subcategory/:subcategoryId/product/:productId', validateQuery, (req, res) => {
    // 处理请求
});

缓存策略

  1. 基于参数和查询字符串的缓存:使用一个唯一的键来标识请求,例如将所有参数和查询字符串拼接成一个哈希值。
const nodeCache = require('node-cache');
const myCache = new nodeCache();

const generateCacheKey = (req) => {
    const { categoryId, subcategoryId, productId } = req.params;
    const { color, size } = req.query;
    return `${categoryId}:${subcategoryId}:${productId}:${color}:${size}`;
};

app.get('/category/:categoryId/subcategory/:subcategoryId/product/:productId', validateQuery, (req, res) => {
    const cacheKey = generateCacheKey(req);
    const cachedData = myCache.get(cacheKey);
    if (cachedData) {
        return res.send(cachedData);
    }
    // 处理请求,获取数据
    const data = { message: 'Processed data' };
    myCache.set(cacheKey, data);
    res.send(data);
});
  1. 缓存失效策略:可以设置缓存的过期时间,或者在数据发生变化时手动清除缓存。
// 设置缓存过期时间为60秒
myCache.set(cacheKey, data, 60);

异步处理

  1. 使用async/await:确保在处理数据库查询或其他异步操作时,代码保持同步风格,避免回调地狱。
const Product = require('../models/product');

app.get('/category/:categoryId/subcategory/:subcategoryId/product/:productId', validateQuery, async (req, res) => {
    const cacheKey = generateCacheKey(req);
    const cachedData = myCache.get(cacheKey);
    if (cachedData) {
        return res.send(cachedData);
    }
    try {
        const product = await Product.findById(req.productId).where('color', req.query.color).where('size', req.query.size);
        myCache.set(cacheKey, product);
        res.send(product);
    } catch (error) {
        res.status(500).send('Error fetching product');
    }
});
  1. 并发异步操作:如果有多个异步操作可以并行执行,可以使用Promise.all来提高性能。
const Category = require('../models/category');
const Subcategory = require('../models/subcategory');
const Product = require('../models/product');

app.get('/category/:categoryId/subcategory/:subcategoryId/product/:productId', validateQuery, async (req, res) => {
    const cacheKey = generateCacheKey(req);
    const cachedData = myCache.get(cacheKey);
    if (cachedData) {
        return res.send(cachedData);
    }
    try {
        const [category, subcategory, product] = await Promise.all([
            Category.findById(req.categoryId),
            Subcategory.findById(req.subcategoryId),
            Product.findById(req.productId).where('color', req.query.color).where('size', req.query.size)
        ]);
        const combinedData = { category, subcategory, product };
        myCache.set(cacheKey, combinedData);
        res.send(combinedData);
    } catch (error) {
        res.status(500).send('Error fetching data');
    }
});