NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发

2.1 Express 的中间件机制?如何组织一个 RESTful API 项目?

面试官好,我来讲讲 Express 的中间件机制,它是 Express 架构的核心,也是组织 RESTful 项目的基础。

🧩 什么是中间件?

中间件(Middleware)就是一类函数,它们可以对 reqres 进行加工、拦截、判断,决定是否将请求传递给下一个处理函数。

✨ 形式:

function (req, res, next) { ... }

  • req:请求对象;

  • res:响应对象;

  • next():调用下一个中间件。

🧱 中间件执行顺序是按注册顺序的:

复制代码
app.use(logger);
app.use(auth);
app.get('/user', controller);

📦 常见中间件分类:

类型 示例
应用级 app.use() 全局中间件
路由级 router.use()、局部中间件
错误处理中间件 4 个参数 (err, req, res, next)
内置中间件 express.json()static()
第三方中间件 corsbody-parsermorgan

📐 如何组织一个 RESTful 项目?

文件结构推荐:

复制代码
src/
├── routes/
│   └── user.js
├── controllers/
│   └── userController.js
├── middlewares/
│   └── auth.js
├── services/
│   └── userService.js
├── utils/
├── app.js

核心思想:路由-控制器-服务分离,中间件集中注册。

复制代码
// routes/user.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const auth = require('../middlewares/auth');

router.get('/profile', auth, userController.getProfile);
module.exports = router;

2.2 如何处理接口异常?如何做统一异常拦截?

在 Express 和 NestJS 中,我分别有两种做法来实现"统一异常处理",这对于生产级项目来说是必不可少的。

✅ Express:统一异常处理

Express 中通过定义 错误处理中间件 来统一拦截异常。

复制代码
// app.js
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ code: 500, message: 'Internal Server Error' });
});

在路由/服务中使用 next(err) 触发:

复制代码
try {
  throw new Error('DB Error');
} catch (err) {
  next(err);
}

✅ NestJS:内置异常过滤器机制

NestJS 提供 @Catch() 装饰器,可以创建自定义异常过滤器。

复制代码
// common/filters/http-exception.filter.ts
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const res = ctx.getResponse<Response>();
    const status = exception.getStatus();

    res.status(status).json({
      statusCode: status,
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}

注册方式:

复制代码
// main.ts
app.useGlobalFilters(new HttpExceptionFilter());

2.3 JWT 鉴权的完整流程?Token 存储在哪里最安全?

我来详细说明一个完整的 JWT 登录鉴权流程,以及 token 应该如何存储最安全。

🔐 JWT 基本结构:

JWT 分为三段:

Header.Payload.Signature

  • Header:加密算法

  • Payload:用户数据(如 userId, role)

  • Signature:签名,防篡改


✅ JWT 鉴权流程:

  1. 用户登录时提交用户名 + 密码;

  2. 服务端验证后,使用 jsonwebtoken 生成 Token:

const token = jwt.sign({ userId: 123 }, secret, { expiresIn: '1h' });

  1. 客户端收到后,将 Token 储存并在后续请求中带上:

Authorization: Bearer <token>

  1. 服务端中间件负责解析、验证 Token 并附加用户信息:

    const decoded = jwt.verify(token, secret);
    req.user = decoded;


✅ Token 存储方案对比:

存储位置 安全性 建议使用场景
LocalStorage 易被 XSS 获取 适合开发调试、测试环境
Cookie(HttpOnly) 安全、防 XSS ✅ 最安全,推荐生产环境
SessionStorage 随刷新丢失 不适合长期登录会话

2.4 如何实现接口的权限控制?支持不同角色访问不同资源?

我习惯使用"路由权限 + 用户角色"的结合方式来实现精细化权限控制。

🎯 实现思路:

  1. 登录时,在 JWT 中记录用户的 rolepermissions

  2. 使用中间件或守卫(Guard)读取用户角色,匹配当前路由所需权限;

  3. 不满足则返回 403 Forbidden。


✅ Express 示例:

复制代码
function roleGuard(roles) {
  return (req, res, next) => {
    const userRole = req.user.role;
    if (roles.includes(userRole)) next();
    else res.status(403).json({ message: 'Forbidden' });
  };
}

router.get('/admin', auth, roleGuard(['admin']), adminController.doSomething);

✅ NestJS 示例(守卫 + 装饰器):

复制代码
// roles.decorator.ts
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

// roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    const user = context.switchToHttp().getRequest().user;
    return roles.includes(user.role);
  }
}

使用:

复制代码
@UseGuards(RolesGuard)
@Roles('admin')
@Get('/admin')
getAdminData() { ... }

2.5 如何在 NestJS 中使用装饰器、模块、依赖注入机制?

NestJS 的核心就是基于模块化的架构、装饰器驱动和依赖注入。我详细说明下。


✅ 模块(Module)

每个模块(@Module)组织了 controller、service、provider 的集合。

复制代码
@Module({
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService]
})
export class UserModule {}

✅ 控制器(Controller)

控制请求和响应,对应 RESTful 接口。

复制代码
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  findAll() {
    return this.userService.findAll();
  }
}

✅ 服务(Service)+ 依赖注入(DI)

复制代码
@Injectable()
export class UserService {
  findAll() { return [{ name: 'Tom' }]; }
}

Service 是业务逻辑层,通过构造函数注入。

// Controller 中自动注入

constructor(private readonly userService: UserService) {}


✅ 自定义装饰器(Decorator)

用于封装通用逻辑,如读取 token 中用户 ID。

复制代码
export const CurrentUser = createParamDecorator(
  (data, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

使用:

复制代码
@Get('/profile')
getProfile(@CurrentUser() user) {
  return user;
}

✅ 总结对照表:

问题 技术点
2.1 Express 中间件机制、项目结构设计
2.2 错误处理中间件 vs Nest 异常过滤器
2.3 JWT 鉴权流程、Token 安全存储
2.4 RBAC 角色权限控制实现方式
2.5 Nest 模块系统、依赖注入、装饰器应用

相关推荐
咖啡の猫1 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲3 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5814 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路4 小时前
GeoTools 读取影像元数据
前端
ssshooter4 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友5 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry5 小时前
Jetpack Compose 中的状态
前端
dae bal6 小时前
关于RSA和AES加密
前端·vue.js
柳杉6 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog6 小时前
低端设备加载webp ANR
前端·算法