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

相关推荐
八戒社2 分钟前
如何使用插件和子主题添加WordPress自定义CSS(附:常见错误)
前端·css·tensorflow·wordpress
xzboss14 分钟前
DOM转矢量PDF
前端·javascript
一无所有不好吗14 分钟前
纯前端vue项目实现版本更新(纯代码教程)
前端
安全系统学习28 分钟前
内网横向之RDP缓存利用
前端·安全·web安全·网络安全·中间件
Hilaku35 分钟前
为什么我不再相信 Tailwind?三个月重构项目教会我的事
前端·css·前端框架
FogLetter36 分钟前
JavaScript 的历史:从网页点缀到改变世界的编程语言
前端·javascript·http
鹏北海38 分钟前
Vue3+TS的H5项目实现微信分享卡片样式
前端·微信
轻颂呀40 分钟前
进程——环境变量及程序地址空间
前端·chrome
lyc23333343 分钟前
鸿蒙Stage模型:轻量高效的应用架构「舞台革命」🎭
前端
lyc23333343 分钟前
鸿蒙开发必备:应用配置的「黄金法则」📝
前端