实战项目案例:编写任务管理系统后端API

下面编写一个任务管理系统的后端 API。任务管理系统的界面如图22-3所示。它拥有添加任务、查看任务、设置任务为完成状态以及删除任务的功能。

image 2024 02 20 12 59 53 788
Figure 1. 图22-3 任务管理系统的界面

编写的后端 API 将供下一章将要介绍的前端界面使用。在正式编写之前,先在项目目录下执行以下安装命令,安装 Express 库及其声明文件,以及 cors 库及其声明文件(cors 库是 Express 的扩展中间件,用于支持跨域访问)。

$ npm install express cors
$ npm install @types/express @types/cors -D
bash

项目结构如下所示,粗体字标出的文件表示相对于上一节新增或修改的文件。

D:\TSProject\server-side
│  package.json
│  tsconfig.json
│
├─node_modules
│      ...
│
├─dist
│      ...
│
└─src
        index.ts
        type.d.ts
        TaskAccess.ts
bash

编写任务类型声明并实现任务数据访问功能

src/type.d.ts 文件的内容如下。

interface Task {
    id: number,
    name: string,
    description: string,
    isDone:boolean
}
typescript

该文件定义了表示任务的 Task 接口,用于供其他 TypeScript 文件引用,它包含 idname(名称)、description(描述)、isDone(是否完成)等字段。

src/TaskAccess.ts 文件的内容如下。

class TaskAccessor {
    tasks: Task[] = [{ id: 1, name: "完成报告", description: "完成上个月的工作报告", isDone:
    false }];
    taskIdIndex = 1;

    addTask(task: Task): Task {
        let newTask = {
            id: ++this.taskIdIndex,
            name: task.name,
            description: task.description,
            isDone: false
        };
        this.tasks.push(newTask);
        return newTask;
    }

    deleteTask(taskId: number): boolean {
        let index = this.tasks.findIndex(p => p.id == taskId)
        if (index < 0) {
            return false;
        }
        this.tasks.splice(index, 1);
        return true;
    }

    setTaskDone(taskId: number): boolean {
        let index = this.tasks.findIndex(p => p.id == taskId)
        if (index < 0) {
            return false;
        }
        this.tasks[index].isDone = true;
        return true;
    }
}

export const taskAccessor = new TaskAccessor();
typescript

该文件提供 Task 数据访问功能。其中,声明了 TaskAccessor 类,在代码最后实例化了该类并将其值赋给变量 taskAccessor,然后以模块的形式导出。

下面分别介绍 TaskAccess 类中的各个成员。

  • tasks 属性:用于存放 Task 数组,并在其中初始化一个名为 “完成报告” 的 Task

  • taskIdIndex 属性:用于存放当前最大的 Task id,用于实现新建 Taskid 自增。

  • addTask() 方法:要求传入新增的 Task 对象,传入的 Task 对象中需要包含 name 属性和 description 属性。id 属性根据 taskIdIndex 自增,而 isDone 属性默认为 false。新的 Task 对象将放到 tasks 数组中,返回值为当前新增的 Task 对象。

  • deleteTask() 方法:要求传入待删除的 Task id,返回值表示是否成功删除。如果没有找到匹配传入的 idTask,则表示删除失败,返回 false;如果找到,则执行删除操作并返回 true

  • setTaskDone() 方法:要求传入已完成的 Task id,返回值表示是否成功将 Task 设置为已完成。如果没有找到匹配传入 idTask,则表示设置失败,返回 false;如果找到,则将 TaskisDone 属性设置为 true 并返回 true

编写任务管理后端服务API

src/index.ts 文件的内容如下。

import express from 'express';
import cors from 'cors'
import { taskAccessor } from './TaskAccess'
const app = express();
const port = 8000;

//由于各个路由的请求中涉及Json对象转换,因此需要引入json中间件
app.use(express.json());
//下一章中的前端会调用下面的API,涉及跨域访问,需引入cors中间件
app.use(cors());

app.get('/tasks', (req, res) => {
    res.send(taskAccessor.tasks);
});

app.post('/task', (req, res) => {
    const { name, description } = req.body;
    if (!name?.trim() || !description?.trim()) {
        return res.status(400).send('Name or description is null.');
    }
    let newTask = taskAccessor.addTask(req.body);
    res.status(200).send(newTask);
});

app.delete('/task/:id', (req, res) => {
    let deleteSuccess = taskAccessor.deleteTask(Number(req.params.id))
    if (!deleteSuccess) {
        return res.status(400).send('Task does not exist.');
    }
    res.status(200).send(deleteSuccess);
});

app.put('/task/:id', (req, res) => {
    let setSuccess = taskAccessor.setTaskDone(Number(req.params.id))
    if (!setSuccess) {
        return res.status(400).send('Task does not exist.');
    }
    res.status(200).send(setSuccess);
});

app.listen(port, () => {
    return console.log(`Express is listening at http://localhost:${port}`);
});
typescript

该文件用于提供后端服务,引用 TaskAccess 模块来查询或修改 Task 数据,并将通过不同路由发布不同的数据操作 API。

接下来,分别介绍 index.ts 文件中各方法的作用。

  • app.use(…​):引入中间件。这里引入了两个中间件,分别为 jsoncorsjson 中间件用于处理各个路由的请求中涉及的 Json 对象转换,cors 中间件用于支持下一章中将要编写的前端界面以跨域形式去调用各个 API。

  • app.get('/tasks'…​):获取全部的 Task 数据的 API。

  • app.post('/task'…​):创建 Task 的 API。如果传入的 name 属性和 description 属性为空值,将返回 HTTP 状态码 400;如果传入的 Task 内容正确,则调用 taskAccessoraddTask() 方法新增 Task,然后返回新增的 Task,且 HTTP 状态码为 200。

  • app.delete('/task/:id'…​):删除 Task 的 API,将调用 taskAccessordeleteTask() 方法。如果删除失败,则返回 HTTP 状态码 400;如果成功,则返回 true,且 HTTP 状态码为 200。

  • app.put('/task/:id'…​):设置 Task 状态为已完成的 API,将调用 taskAccessorsetTaskDone() 方法。如果设置失败,则返回 HTTP 状态码 400;如果成功,则返回 true,且 HTTP 状态码为 200。

之后,你就可以使用如 Postman 等 API 工具访问这些 API。首先,执行 npm start 命令启动后端服务。然后,通过 Postman 调用 /task POST API,新增一个任务,如图22-4所示。接着,调用 /task/:id PUT API 设置任务处于完成状态,如图22-5所示。

image 2024 02 20 13 48 56 401
Figure 2. 图22-4 调用/task POST API新增一个任务
image 2024 02 20 13 49 21 848
Figure 3. 图22-5 调用/task/:id PUT API设置任务处于完成状态

最后,调用 /tasks GET API 获取当前的全部任务,如图22-6所示。

image 2024 02 20 13 49 54 801
Figure 4. 图22-6 调用/tasks GET API获取当前的全部任务