Deno 是一个可用于构建服务器的 JavaScript/TypeScript 运行时环境,它提供了丰富的标准库和安全的运行环境。RESTful API 是现代 Web 应用程序的重要组成部分,它能够帮助我们有效地组织和管理各种资源。在本篇文章中,我们将介绍如何使用 Deno 来实现一个 RESTful API。
准备工作
在开始实现 RESTful API 之前,需要先安装 Deno。你可以从官网下载安装程序,也可以使用包管理器来安装。安装完成后,我们就可以开始编写代码了。
设计 API 接口
在开始编写代码之前,我们需要先设计出我们的 API 接口。一个合理的接口设计应该具备以下特点:
- 简洁明了:一个接口应该只做一件事情。
- 直观友好:接口名称应该能够反映其功能。
- 安全可靠:接口应该能够验证用户的身份,并保证数据的安全性。
- 可扩展:接口应该能够方便地扩展和升级。
在本篇文章中,我们将实现一个简单的任务管理系统,它包括以下接口:
- GET /tasks:获取所有任务列表。
- GET /tasks/:id:获取指定 ID 的任务信息。
- POST /tasks:创建一个新的任务。
- PUT /tasks/:id:更新指定 ID 的任务信息。
- DELETE /tasks/:id:删除指定 ID 的任务。
编写代码
在我们确定了 API 接口之后,我们就可以开始编写代码了。首先我们需要创建一个服务器,并监听一个端口:
import { serve } from "https://deno.land/std/http/server.ts"; const server = serve({ port: 8080 }); console.log(`Server running at http://localhost:8080/`); for await (const req of server) { req.respond({ body: "Hello Deno\n" }); }
我们可以使用上述代码来启动服务器,当用户访问 http://localhost:8080 时,服务器将返回 "Hello Deno"。现在我们需要为每个接口创建一个处理函数。
处理函数
我们可以通过创建一个对象来存储我们的接口处理函数,该对象包含每个接口的方法和路径:
// javascriptcn.com code example interface Route { method: string; path: string; handler: (req: Request) => void; } const routes: Route[] = [ { method: "GET", path: "/tasks", handler: (req: Request) => { // 处理 GET /tasks 请求 }, }, { method: "GET", path: "/tasks/:id", handler: (req: Request) => { // 处理 GET /tasks/:id 请求 }, }, { method: "POST", path: "/tasks", handler: (req: Request) => { // 处理 POST /tasks 请求 }, }, { method: "PUT", path: "/tasks/:id", handler: (req: Request) => { // 处理 PUT /tasks/:id 请求 }, }, { method: "DELETE", path: "/tasks/:id", handler: (req: Request) => { // 处理 DELETE /tasks/:id 请求 }, }, ];
我们可以使用路由来将请求映射到正确的处理函数。
获取请求数据
在处理请求之前,我们需要从请求中获取数据。我们可以使用 Deno.readAll()
来读取请求的主体数据。我们还需要解析 URL 参数以及请求体数据。以下代码演示了如何获取 GET 请求中的参数:
const searchParams = new URLSearchParams(req.url.split("?")[1]); const id = searchParams.get("id");
如果您要处理 POST 或 PUT 请求,则需要从请求主体中读取数据。以下是一种解析 JSON 数据的方式:
const body = await req.body().value; const data = JSON.parse(body);
始终要确保验证请求中的数据格式,否则可能会引发安全问题。我们还应该对请求进行身份验证,以确保用户是经过身份验证的。
实现 RESTful API
现在让我们来实现每个 API 接口的处理函数。
GET /tasks
我们想要从服务器中的某个地方返回任务列表。这可以通过简单地使用 Deno.readTextFileSync()
从文件中读取 JSON 数据来实现:
// javascriptcn.com code example const tasks = JSON.parse( Deno.readTextFileSync("./data/tasks.json"), ); const getTasks = () => { return new Response(JSON.stringify(tasks), { headers: { "Content-Type": "application/json" }, }); };
现在我们可以在 GET /tasks 处理程序中调用 getTasks()
并返回任务列表。
GET /tasks/:id
我们想要查找并返回一个特定的任务。这可以通过单独的 findTask()
函数来实现:
// javascriptcn.com code example const findTask = (id: string) => { return tasks.find((task) => task.id === id); }; const getTaskById = (id: string) => { const task = findTask(id); if (!task) { return new Response(`Task ${id} not found`, { status: 404 }); } return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, }); };
我们可以在 GET /tasks/:id 处理程序中调用 getTaskById()
函数并返回找到的任务。
POST /tasks
我们想要创建一个新的任务并将其添加到任务列表中。请使用以下代码实现:
// javascriptcn.com code example const createTask = (data: any) => { const id = v4.generate(); const task = { id, ...data }; tasks.push(task); Deno.writeTextFileSync("./data/tasks.json", JSON.stringify(tasks)); return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, status: 201, }); }; const postTask = async (req: Request) => { data = await req.json(); const newTask = createTask(data); return newTask; };
我们可以在 POST /tasks 处理程序中调用 postTask()
函数并将创建的任务返回。
PUT /tasks/:id
我们想要更新一个任务的数据。请使用以下代码实现:
// javascriptcn.com code example const updateTask = (id: string, data: any) => { const task = findTask(id); if (!task) { return new Response(`Task ${id} not found`, { status: 404 }); } Object.assign(task, data); Deno.writeTextFileSync("./data/tasks.json", JSON.stringify(tasks)); return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, }); }; const putTaskById = async (req: Request, id: string) => { data = await req.json(); const updatedTask = updateTask(id, data); return updatedTask; };
我们可以在 PUT /tasks/:id 处理程序中调用 putTaskById()
函数并传递要更新的任务的 ID。
DELETE /tasks/:id
我们想要删除一个任务。请使用以下代码实现:
// javascriptcn.com code example const deleteTask = (id: string) => { const index = tasks.findIndex((task) => task.id === id); if (index === -1) { return new Response(`Task ${id} not found`, { status: 404 }); } const [task] = tasks.splice(index, 1); Deno.writeTextFileSync("./data/tasks.json", JSON.stringify(tasks)); return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, }); }; const deleteTaskById = (id: string) => { const deletedTask = deleteTask(id); return deletedTask; };
我们可以在 DELETE /tasks/:id 处理程序中调用 deleteTaskById()
函数并传递要删除的任务的 ID。
结论
在本篇文章中,我们介绍了如何在 Deno 中实现 RESTful API。通过实现我们的任务管理系统,我们可以学习到如何使用 Deno 标准库和第三方库来处理 HTTP 请求和响应、路由、JSON 数据解析等技术。我们还介绍了如何编写安全可靠的接口和如何设计可扩展的接口。如果你想了解更多关于 Deno 的信息,请查看官方文档。
示例代码
以下是完整的示例代码:
// javascriptcn.com code example import { serve } from "https://deno.land/std/http/server.ts"; import { v4 } from "https://deno.land/std/uuid/mod.ts"; const server = serve({ port: 8080 }); console.log(`Server running at http://localhost:8080/`); interface Task { id: string; title: string; description?: string; completed: boolean; } const tasks: Task[] = JSON.parse( Deno.readTextFileSync("./data/tasks.json"), ); interface Route { method: string; path: string; handler: (req: Request, params: { [key: string]: string }) => void; } const routes: Route[] = [ { method: "GET", path: "/tasks", handler: (req: Request) => { const tasks = getTasks(); req.respond(tasks); }, }, { method: "GET", path: "/tasks/:id", handler: (req: Request, params: { [key: string]: string }) => { const task = getTaskById(params.id); req.respond(task); }, }, { method: "POST", path: "/tasks", handler: async (req: Request) => { const task = await postTask(req); req.respond(task); }, }, { method: "PUT", path: "/tasks/:id", handler: async (req: Request, params: { [key: string]: string }) => { const updatedTask = await putTaskById(req, params.id); req.respond(updatedTask); }, }, { method: "DELETE", path: "/tasks/:id", handler: (req: Request, params: { [key: string]: string }) => { const deletedTask = deleteTaskById(params.id); req.respond(deletedTask); }, }, ]; const getTasks = () => { return new Response(JSON.stringify(tasks), { headers: { "Content-Type": "application/json" }, }); }; const findTask = (id: string) => { return tasks.find((task) => task.id === id); }; const getTaskById = (id: string) => { const task = findTask(id); if (!task) { return new Response(`Task ${id} not found`, { status: 404 }); } return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, }); }; const createTask = (data: any) => { const id = v4.generate(); const task = { id, ...data }; tasks.push(task); Deno.writeTextFileSync("./data/tasks.json", JSON.stringify(tasks)); return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, status: 201, }); }; const postTask = async (req: Request) => { data = await req.json(); const newTask = createTask(data); return newTask; }; const updateTask = (id: string, data: any) => { const task = findTask(id); if (!task) { return new Response(`Task ${id} not found`, { status: 404 }); } Object.assign(task, data); Deno.writeTextFileSync("./data/tasks.json", JSON.stringify(tasks)); return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, }); }; const putTaskById = async (req: Request, id: string) => { data = await req.json(); const updatedTask = updateTask(id, data); return updatedTask; }; const deleteTask = (id: string) => { const index = tasks.findIndex((task) => task.id === id); if (index === -1) { return new Response(`Task ${id} not found`, { status: 404 }); } const [task] = tasks.splice(index, 1); Deno.writeTextFileSync("./data/tasks.json", JSON.stringify(tasks)); return new Response(JSON.stringify(task), { headers: { "Content-Type": "application/json" }, }); }; const deleteTaskById = (id: string) => { const deletedTask = deleteTask(id); return deletedTask; }; for await (const req of server) { const { pathname, searchParams, method } = new URL(req.url, "http://localhost"); const route = routes.find((route) => route.method === method && route.path === pathname); if (route) { const params: { [key: string]: string } = {}; route.path.split("/").forEach((part, i) => { const match = part.match(/^:(.*)$/); if (match) { params[match[1]] = pathname.split("/")[i]; } }); route.handler(req, params); } else { req.respond({ status: 404 }); } }
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67367dac0bc820c58254a86b