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 模块系统、依赖注入、装饰器应用

相关推荐
ywf12152 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭2 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf8 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特8 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷8 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian9 小时前
前端node常用配置
前端
华洛9 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq9 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A10 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常10 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端