阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > DTO 与数据格式转换

DTO 与数据格式转换

作者:陈川 阅读数:23786人阅读 分类: Node.js

DTO 与数据格式转换

DTO(Data Transfer Object)是一种设计模式,用于在不同层之间传输数据。在 Koa2 中,DTO 常用于规范化前后端交互的数据格式,确保数据的完整性和一致性。数据格式转换则是将原始数据转换为符合业务需求的格式,比如将数据库查询结果转换为前端需要的 JSON 结构。

DTO 的作用与实现

DTO 的核心作用是解耦数据层与业务层,避免直接暴露数据库模型。例如,从数据库查询的用户信息可能包含敏感字段(如密码),而 DTO 可以过滤这些字段,仅返回必要的数据。

// 数据库模型
interface UserModel {
  id: number;
  username: string;
  password: string;
  email: string;
  createdAt: Date;
}

// DTO 结构
interface UserDTO {
  id: number;
  username: string;
  email: string;
}

// 转换函数
function toUserDTO(user: UserModel): UserDTO {
  return {
    id: user.id,
    username: user.username,
    email: user.email,
  };
}

在 Koa2 中,可以通过中间件或工具函数实现 DTO 转换:

import Koa from 'koa';

const app = new Koa();

app.use(async (ctx) => {
  const userFromDB: UserModel = {
    id: 1,
    username: 'test',
    password: '123456',
    email: 'test@example.com',
    createdAt: new Date(),
  };

  ctx.body = toUserDTO(userFromDB); // 返回过滤后的 DTO
});

数据格式转换的场景

数据格式转换不仅限于字段过滤,还包括:

  1. 字段重命名:将数据库的 created_at 转为驼峰式的 createdAt
  2. 类型转换:将 Date 对象转为 ISO 字符串。
  3. 嵌套结构展开:将关联表的数据合并为一个对象。
// 示例:处理日期和嵌套数据
interface PostModel {
  id: number;
  title: string;
  content: string;
  created_at: Date;
  author: UserModel;
}

interface PostDTO {
  id: number;
  title: string;
  content: string;
  createdAt: string;
  author: {
    name: string;
    email: string;
  };
}

function toPostDTO(post: PostModel): PostDTO {
  return {
    id: post.id,
    title: post.title,
    content: post.content,
    createdAt: post.created_at.toISOString(),
    author: {
      name: post.author.username,
      email: post.author.email,
    },
  };
}

使用类验证器实现 DTO 校验

在 Koa2 中,可以结合 class-validator 库实现 DTO 的校验。例如,验证用户注册时的输入:

import { IsEmail, IsString, MinLength } from 'class-validator';

class CreateUserDTO {
  @IsString()
  @MinLength(3)
  username: string;

  @IsEmail()
  email: string;

  @IsString()
  @MinLength(6)
  password: string;
}

// 在中间件中使用
import { validate } from 'class-validator';

app.use(async (ctx) => {
  const dto = new CreateUserDTO();
  Object.assign(dto, ctx.request.body);

  const errors = await validate(dto);
  if (errors.length > 0) {
    ctx.status = 400;
    ctx.body = { errors };
    return;
  }

  // 继续处理业务逻辑
});

复杂数据结构的转换

对于复杂数据结构(如分页查询结果),DTO 可以统一响应格式:

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  pageSize: number;
}

// 分页查询用户列表
async function getUsers(page: number, pageSize: number): Promise<PaginatedResult<UserDTO>> {
  const [users, total] = await UserModel.findAndCount({
    skip: (page - 1) * pageSize,
    take: pageSize,
  });

  return {
    data: users.map(toUserDTO),
    total,
    page,
    pageSize,
  };
}

性能优化与缓存

频繁的数据转换可能影响性能,可以通过缓存机制优化。例如,使用 Map 缓存已转换的 DTO:

const userCache = new Map<number, UserDTO>();

function getCachedUserDTO(user: UserModel): UserDTO {
  if (userCache.has(user.id)) {
    return userCache.get(user.id)!;
  }

  const dto = toUserDTO(user);
  userCache.set(user.id, dto);
  return dto;
}

错误处理与日志记录

在转换过程中,可能因数据异常导致错误。可以通过 try-catch 捕获并记录日志:

app.use(async (ctx) => {
  try {
    const user = await getUserFromDB(ctx.params.id);
    ctx.body = toUserDTO(user);
  } catch (error) {
    ctx.app.emit('error', error, ctx); // 触发错误事件
    ctx.status = 500;
  }
});

// 全局错误监听
app.on('error', (err, ctx) => {
  console.error('DTO conversion error:', err);
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌