Deno@v1.0 稳定版虽然刚发布不久,但Deno社区已经出现一大批跟随者,基于deno的设计理念,本人比较看好deno的前景,所以接下来我们用deno来做个简单的todo-API小例子
安装:(本项目基于Mac环境开发)
安装asdf多版本管理工具(类似nodejs中的NVM):
brew install asdf
配置环境变量: ~/.zshrc
. $(brew --prefix asdf)/asdf.sh
安装asdf插件
asdf plugin-add deno https://github.com/asdf-community/asdf-deno.git
安装deno最新版本
asdf install deno lastest
查看当前deno版本:
deno --version
由于项目简单,所有我们直接在根目录创建两个文件:
|- index.ts
|- data.json
index.ts
import { Application, Context, Router } from "https://deno.land/x/oak/mod.ts";
import { green, cyan, bold, yellow } from 'https://deno.land/std@0.51.0/fmt/colors.ts';
interface Todo {
id: string;
title: string;
completed: boolean;
}
const app = new Application();
const router = new Router();
// 一些配置
const config = {
hostname: '127.0.0.1',
port: 8899,
mockData: './data.json'
}
// 路由
router.get('/', async(ctx, next) => {
ctx.response.body = 'todos list';
})
// 获取todo-list
router.get('/todos', async (ctx, next) => {
const { response } = ctx;
try {
const res = await Deno.readFile(config.mockData);
const resData = JSON.parse(new TextDecoder().decode(res));
response.body = {
status: 'ok',
data: resData
}
} catch (err) {
console.log(err);
response.status = 500;
response.body = {
status: 'fail',
data: []
}
}
});
router.get('/todos/:id', async(ctx, next) => {
const { request, response, params } = ctx;
if(!params.id) {
response.status = 400;
response.body = {
status: 'Invalid id'
};
return
}
try {
const res = await Deno.readFile(config.mockData);
const resData = JSON.parse(new TextDecoder().decode(res));
const getItemData = resData.filter((item: any) => item.id === params.id);
response.body = {
status: 'ok',
data: getItemData
}
} catch (error) {
console.log(error);
response.status = 500;
response.body = {
status: 'fail',
data: []
}
}
});
// 创建新todo
router.post('/todos', async (ctx, next) => {
const { request, response } = ctx;
if(!request.hasBody) {
response.status = 400;
response.body = {
status: 'Invalid request'
}
return
}
try {
console.log('reqBody:',await request.body())
const req = await request.body();
const reqTitle = req.value && req.value.title;
const res = await Deno.readFile(config.mockData);
const resData = JSON.parse(new TextDecoder().decode(res));
const resDataLen = resData.length;
resData.push({
id: resDataLen + 1,
title: reqTitle,
completed: false
});
await Deno.writeFile(config.mockData, new TextEncoder().encode(JSON.stringify(resData)));
response.status = 201;
} catch (err) {
console.log(err);
response.status = 502;
response.body = {
status: 'Failed to create a new todo',
error: err
}
}
});
// 更新todo
router.put('/todos/:id', async(ctx, next) => {
const { request, response, params } = ctx
if(!params.id) {
response.status = 400;
response.body = {
status: 'Invalid id'
};
return
}
if(!request.hasBody) {
response.status = 400;
response.body = {
status: 'Invalid request'
};
return
}
try {
const { value } = await request.body();
const [[_1, title], [_2, completed]] = value;
const res = await Deno.readFile(config.mockData);
const resData = JSON.parse(new TextDecoder().decode(res));
const updateData = resData.map((updateItem: any) => {
if(updateItem.id === params.id) {
return {
...updateItem, title, completed
}
}
return updateItem
});
await Deno.writeFile(config.mockData, new TextEncoder().encode(JSON.stringify(updateData)));
response.status = 204
} catch (error) {
console.log(error);
response.status = 502;
response.body = {
status: 'Failed to update',
error
}
}
});
// 删除todo
router.delete('/todos/:id', async(ctx, next) => {
const {request, response, params} = ctx;
if(!params.id) {
response.status = 400;
response.body = {
status: 'Invalid id'
}
return
}
try {
const res = await Deno.readFile(config.mockData);
const resData = JSON.parse(new TextDecoder().decode(res));
const updateData = resData.filter((item: any) => item.id !== params.id);
await Deno.writeFile(config.mockData, new TextEncoder().encode(JSON.stringify(updateData)));
response.status = 200;
response.body = {
status: 'ok',
data: updateData
};
} catch (error) {
console.log(error);
response.status = 502;
response.body = {
status: 'Faild to update',
error
};
}
});
// Logger
app.use(async (ctx, next) => {
const { request, response } = ctx;
await next();
const rt = response.headers.get('X-Response-Time');
console.log(
`${green(request.method)} ${cyan(request.url.pathname)} - ${bold(String(rt))}`
);
});
// Response Time
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set('X-Response-Time', `${ms}ms`);
});
app.use(router.routes());
app.use(router.allowedMethods());
app.use((ctx, next) => {
const { request, response } = ctx;
response.status = 404;
response.body =
`<html>
<head></head>
<body>
<h1>404 - Not Found</h1>
<p>Path <code>${request.url}</code> not found.</p>
</body>
</html>`;
});
console.log(
bold('Server is running in') + yellow(`${config.hostname}:${config.port}`)
);
await app.listen({ port: config.port });
console.log(bold('Finished.'))
由于deno@v1.0.0版本下对项目中依赖的
https://deno.land/x/oak/mod.ts会报错,所以我们根据官方oak-issues中提供的建议,通过安装deno@v1.0.0-rc3版本来临时解决这个问题(就如deno.land官方所说,not all of Deno's features are ready for production yet)
data.json
[
{"id":5,"title": "AAAAAAAAAA", "completed": false},
{"id":15,"title": "BBBBBBBBBB", "completed": false}
{"id":25,"title": "CCCCCCCCCC", "completed": false}
{"id":35,"title": "DDDDDDDDDD", "completed": false}
]
在项目根目录执行如下命令运行起来:
deno run --allow-net --allow-read --allow-write index.ts
然后我们就可以通过postman 或者 Paw 模拟API接口请求了,是不是很简单呢~~