You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何创建Mapper实现Entity与DTO互转?求Express/NestJS示例

为什么用Mapper类实现DTO与Entity的相互转换?

核心原因与收益

  • 职责拆分更清晰:Entity是和数据库表绑定的模型,管数据持久化;DTO是接口与前端约定的数据结构,管前后端数据交互。Mapper专门负责两者的转换,不让Entity或DTO掺合不属于自己的逻辑,代码维护起来更省心。
  • 消灭重复代码:如果每个接口都手动写{ id: user.id, username: user.username... }这类转换逻辑,重复代码会堆得老高。把转换逻辑集中在Mapper里,改一处就能同步所有用到转换的地方。
  • 避免敏感数据泄露:Entity里难免有密码哈希、权限等级这类敏感字段,Mapper可以直接过滤掉这些字段,不用每次返回数据都手动删除,从根源上防止不小心把敏感数据返回给前端。
  • 适配多场景更灵活:同一个Entity可能对应多个DTO——比如列表页只需要用户名和ID,详情页需要完整信息。Mapper可以写不同的转换方法,不用改Entity本身就能适配不同接口的需求。

NestJS示例

先定义数据库实体(Entity)和数据传输对象(DTO):

// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  passwordHash: string; // 敏感字段,禁止返回前端

  @Column()
  email: string;

  @Column()
  role: string;
}

// user.dto.ts
export class UserDTO {
  id: number;
  username: string;
  email: string;
  role: string;
}

接下来写专门的Mapper类,注入到NestJS的依赖容器中:

// user.mapper.ts
import { Injectable } from '@nestjs/common';
import { UserEntity } from './user.entity';
import { UserDTO } from './user.dto';

@Injectable()
export class UserMapper {
  // 单个Entity转DTO
  toDTO(entity: UserEntity): UserDTO {
    const dto = new UserDTO();
    dto.id = entity.id;
    dto.username = entity.username;
    dto.email = entity.email;
    dto.role = entity.role;
    // 主动忽略passwordHash,保证数据安全
    return dto;
  }

  // DTO转Entity(用于创建/更新用户)
  toEntity(dto: Partial<UserDTO>): UserEntity {
    const entity = new UserEntity();
    entity.username = dto.username;
    entity.email = dto.email;
    entity.role = dto.role || 'user'; // 设置默认角色
    return entity;
  }

  // 批量转换Entity列表
  toDTOList(entities: UserEntity[]): UserDTO[] {
    return entities.map(entity => this.toDTO(entity));
  }
}

在Service中注入Mapper,直接调用转换方法:

// user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from './user.entity';
import { UserDTO } from './user.dto';
import { UserMapper } from './user.mapper';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private userRepo: Repository<UserEntity>,
    private userMapper: UserMapper,
  ) {}

  async getAllUsers(): Promise<UserDTO[]> {
    const users = await this.userRepo.find();
    return this.userMapper.toDTOList(users);
  }

  async createUser(userDTO: UserDTO): Promise<UserDTO> {
    const userEntity = this.userMapper.toEntity(userDTO);
    const savedUser = await this.userRepo.save(userEntity);
    return this.userMapper.toDTO(savedUser);
  }
}

Express示例

Express更轻量化,Mapper可以写成普通工具类,这里用Mongoose作为数据库ORM举例:

首先定义数据库模型(对应Entity):

// user.model.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  username: String,
  passwordHash: String,
  email: String,
  role: String
});

module.exports = mongoose.model('User', userSchema);

编写UserMapper类:

// user.mapper.js
class UserMapper {
  // Entity转返回给前端的DTO
  toDTO(userEntity) {
    return {
      id: userEntity._id.toString(),
      username: userEntity.username,
      email: userEntity.email,
      role: userEntity.role
    };
  }

  // 前端传入的DTO转Entity(用于创建用户)
  toEntity(userDTO) {
    return {
      username: userDTO.username,
      email: userDTO.email,
      role: userDTO.role || 'user'
    };
  }

  // 批量转换
  toDTOList(userEntities) {
    return userEntities.map(entity => this.toDTO(entity));
  }
}

module.exports = new UserMapper();

在路由中使用Mapper处理数据转换:

// user.routes.js
const express = require('express');
const router = express.Router();
const User = require('./user.model');
const userMapper = require('./user.mapper');

router.get('/users', async (req, res) => {
  try {
    const users = await User.find();
    const userDTOs = userMapper.toDTOList(users);
    res.json(userDTOs);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

router.post('/users', async (req, res) => {
  try {
    const userEntity = userMapper.toEntity(req.body);
    const newUser = await User.create(userEntity);
    const userDTO = userMapper.toDTO(newUser);
    res.status(201).json(userDTO);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

module.exports = router;

内容的提问来源于stack exchange,提问作者Normye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:33:15