一个服务
本课属于 Coddy Node.js 学习旅程的“TypeScript 和 NestJS”单元。第 9 课,共 24 课。
控制器自己保存任务,因此应用的其他部分无法访问它们。数据和逻辑应该放在提供者中:Nest 创建这个类,并将它交给需要它的类。
@Injectable()
export class BooksService {
private books = [{ title: 'Dune' }];
}
@Controller('books')
export class BooksController {
constructor(private readonly booksService: BooksService) {}
}@Injectable()用于标记这个类,模块会在providers中列出它。类会在构造函数中写出这个类的名称来请求它:这就是依赖注入。参数前面的private readonly是 TypeScript 的简写:它会创建字段this.booksService,并保存 Nest 传入的内容。Nest 会读取参数的类型。对于带有装饰器的类,编译器会保留这个类型。Nest 会为列出该类的模块创建一个实例,并将同一个实例传给该模块中所有请求它的类。数据会被共享,测试时也可以传入一个模拟对象。
本课一开始,控制器已经在调用this.tasksService。
挑战
简单这一步会打开课程提供的 Nest 任务跟踪器项目,而不是你在 Status Codes and Errors 中的项目文件(包括 src/tasks/tasks.controller.ts)。你之前的工作会保留在原来的课中。编辑 src/tasks/tasks.service.ts 之前,先阅读提供的文件。这份项目副本包含 Nest 任务路由,控制器已准备好调用服务。
任务保存在控制器中,因此应用的其他部分无法访问它们。把任务移到服务中:服务是一个拥有这些任务的类,Nest 会创建一个实例,并将它传给每个需要它的类。控制器已经改写为调用服务。你要完成服务并连接这两部分。
- 在
src/tasks/tasks.service.ts中,使用来自@nestjs/common的@Injectable()标记类TasksService。这样 Nest 就能创建它并提供给其他类。 - 在同一个类中,编写方法
create(title)。它会创建一个任务,包含下一个可用的 id(比this.tasks中最大的 id 大 1)、该标题以及done: false。它会把任务添加到this.tasks并返回该任务。控制器的POST /tasks会使用请求正文中的标题调用它。 - 编写方法
remove(id),其中id是一个数字。如果没有任务的 id 与它匹配,方法必须像findOne一样返回 404:先调用this.findOne(id),它会抛出异常。然后从this.tasks中移除该任务。它不返回任何内容。 - 在
src/tasks/tasks.module.ts中,从./tasks.service导入TasksService,然后在模块中添加一个providers列表,与controllers并列,并将TasksService放入其中。Nest 只会创建模块在这里列出的类。 - 在
src/tasks/tasks.controller.ts中,将TasksController内的注释替换为一个构造函数,该构造函数接收一个参数,写作private readonly tasksService: TasksService。Nest 会识别类型TasksService,并传入它创建的服务实例。参数前面的private readonly也会创建一个字段this.tasksService,用于保存传入的值。下面的方法已经使用这个名称。
试试看:向 /tasks 发送 POST 请求,正文为 { "title": "Buy milk" }。你会得到状态码 201 和:
{ "id": 4, "title": "Buy milk", "done": false }发送 GET /tasks:会得到 4 个任务,Buy milk 排在最后。发送 DELETE /tasks/4:会得到 204。发送 DELETE /tasks/99:会得到 404,消息为 "message": "Task 99 not found"。
亲手试试
import { NotFoundException } from '@nestjs/common';
export interface Task {
id: number;
title: string;
done: boolean;
createdAt?: string;
}
// 让这个类成为可供 Nest 注入的提供者。
export class TasksService {
private tasks: Task[] = [
{ id: 1, title: 'Write the README', done: true, createdAt: '2026-01-05T09:00:00.000Z' },
{ id: 2, title: 'Fix the login bug', done: false, createdAt: '2026-01-06T14:30:00.000Z' },
{ id: 3, title: 'Plan the sprint', done: false, createdAt: '2026-01-07T08:15:00.000Z' },
];
findAll(done?: string): Task[] {
if (done === undefined) {
return this.tasks;
}
return this.tasks.filter((task) => task.done === (done === 'true'));
}
findOne(id: number): Task {
const task = this.tasks.find((task) => task.id === id);
if (!task) {
throw new NotFoundException(`Task ${id} not found`);
}
return task;
}
// 在这里编写 create(title) 和 remove(id)。
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
TypeScript 和 NestJS中的所有课
自己练习: Express 在线编辑器