阶段说明
第一阶段,先不考虑具体业务,纯后端开发API。
目标:
- 手写一个用户信息服务,包含完整的增删改查功能
- 持久化数据,保障服务重启后数据不丢失
- 异常处理
- 参数校验
- 学会使用oak_nest框架
oak是Deno的一个web服务框架,它是参考的Node.js的koa,二者高度相似,语法使用上没有太大差异。
想对二者以及Express、中间件有所了解的话,可以看看这篇《从koa到oak》。
hello world
新建一个文件src/main.ts,内容如下:
import { Application } from "https://deno.land/x/oak@v11.1.0/mod.ts";
const app = new Application();
app.use((ctx) => {
ctx.response.body = "Hello World";
});
app.addEventListener("listen", ({ port }) => {
console.log(`Listening on: "http://localhost:${port}"`);
});
await app.listen({ port: 8000 });
新建deno.jsonc文件:
{
"version": "0.0.0",
"name": "deno_blog",
"tasks": {
"dev": "deno run --allow-net --check --watch src/mod.ts",
}
}
tasks相当于Node.js的script,也是为简化我们输入的命令而生的。你直接在命令行输入具体命令也是一样的。
Deno为解决抛弃了Package.json的后遗症,不得不找个文件来管理部分元数据,于是选择了deno.json或deno.jsonc文件。
- --allow-net表示允许网络请求
- --check表示运行时会校验TypeScript类型,默认不校验
- --watch表示监听文件变化
在命令行中敲以下命令:deno task dev,可以看到控制台会印以下内容:
Task dev deno run --allow-net --check --watch src/main.ts
Watcher Process started.
Check file:///wk/deno/deno_blog/mod.ts
Listening on: "http://localhost:8000"
在浏览器输入http://localhost:8000,可以看到Hello World。
提交git
我们的代码使用git init初始化,并提交。
使用路由
为什么要使用路由?说到底是为了组装代码,所有的业务逻辑集中到一块,很难维护。所以现在不管是后端还是前端开发,基本都要使用路由。
我们先写个用户的增删改查,了解下它的用法。
还是刚才的mod.ts。
1. 定义一个User的接口
interface User {
id: number;
author: string;
age: number;
}
2. 用Map维护用户信息
const users = new Map<number, User>();
const user1: User = {
id: 1,
author: "张三",
age: 18,
};
users.set(user1.id, user1);
3. 把路由加上
import { Application, Router } from "https://deno.land/x/oak@v11.1.0/mod.ts";
const router = new Router();
router
.get("/", (context) => {
context.response.body = "hello world";
})
.get("/user", (context) => {
context.response.body = Array.from(users.values());
})
.get("/user/:id", (context) => {
const id = Number(context.params.id);
if (users.has(id)) {
context.response.body = users.get(id);
} else {
context.response.status = 404;
context.response.body = "user not found";
}
})
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
app.addEventListener("listen", ({ port }) => {
console.log(`Listening on: http://localhost:${port}`);
});
await app.listen({ port: 8000 });
在浏览器输入http://localhost:8000/,打印的还是hello world。
再输入http://localhost:8000/user/,这次就不一样了:
再输入http://localhost:8000/user/1,看到的只有张三的信息:
而输入http://localhost:8000/user/2,看到的是user not found。这个标签页不要关闭。
4. 增加一个用户
在router.get()的最后再添加.post,也可以另起一行,直接router.post。
router.post("/user", async (context) => {
const result = context.request.body({
type: "json",
});
const value = await result.value;
value.id = users.size + 1;
users.set(value.id, value);
context.response.body = value;
});
在刚才的浏览器标签页打开F12,在控制台敲以下代码,回车:
fetch("/user", {
method: "post",
headers: { "content-type": "application/json" },
body: JSON.stringify({ author: "李四", age: 10 }),
});
之后,再刷新,是不是看到页面变了?
这就完成了一个增加接口。你再打开http://localhost:8000/user/,看到的就是2个用户了:
5. 修改用户信息
在RESTFul API中,修改操作是使用put。如果你不使用REST风格,使用post也是可以的。
router.put("/user/:id", async (context) => {
const id = Number(context.params.id);
if (!users.has(id)) {
context.response.status = 404;
context.response.body = "user not found";
return;
}
const result = context.request.body({
type: "json",
});
const value = await result.value;
const user = users.get(id);
user!.age = value.age;
context.response.body = "update ok";
});
注意:我们修改完代码后,因为我们启动命令用了--watch,Deno会自动重启服务,这时新增的用户已经丢失了。因为目前所有操作都是在内存中的,重启服务后内存就重置了,这就是为什么数据需要持久化。
仍是在浏览器的控制台,输入以下代码并执行:
fetch("/user/1", {
method: "put",
headers: { "content-type": "application/json" },
body: JSON.stringify({ age: 5 }),
});
刷新页面,看到http://localhost:8000/user/的变化,用户张三的age已经变成5了。
6. 删除用户
router.delete("/user/:id", (context) => {
const id = Number(context.params.id);
if (users.has(id)) {
users.delete(id);
context.response.body = "delete ok";
} else {
context.response.status = 404;
context.response.body = "user not found";
}
});
刷新页面,你看到的http://localhost:8000/user/仍是原来的数据。
在F12输入执行:
fetch("/user/1", {
method: "delete",
body: null,
});
再刷新页面,只剩下一个空数组了。
这样,一个用户列表的完整的增删改查过程就完成了。有兴趣的,可以不断重复上面的过程,增加新的用户、修改并删除。
作业
现在我们所有代码都写在src/main.ts里,它太臃肿了,你有什么好的主意吗?