Deno第一个稳定版本1.0已经在2020年五月发布了. Deno是一个使用Rust开发的V8运行时, 允许用户直接使用TypeScript开发. 相比于它的前辈Node.js, Deno提供了开箱即用的标准库以及更高的安全运行环境. 尽管还有很长的路要走, 但不能否认Deno为TypeScript使用者提供了一个新的后端开发选择. 本文我们就来初探一下Deno的http标准库, 使用它配合Docker开发一个简单的API服务.
本文英文原版发布于345Tool Blog, 引用请标明出处
TLDR: 所有可执行代码都可以在这里找到
我们要做什么
我们要搭建一个典型的API后端服务, 服务会根据不同Http请求调用相关的CRUD接口来操作数据. 服务主要包括一下几大部分:
虚拟数据层 - 为了简化流程, 这里我们用一个虚拟的用户数据层来模仿数据库DAO操作, 该层会提供一些方法来允许修改数据.
Http服务器- 使用Deno搭建的API Http服务, 也是我们主要搭建的东西. 它暴露了两个API接口GET /users和POST /user.
Docker镜像 - 最终我们会使用Docker把我们的Deno容器化.
用户数据服务
为了简化, 在用户服务里我们使用一个用户列表来模仿数据库里的数据, 使用
addUser方法来向用户列表添加新用户
, 使用listUsers方法来读取所有用户信息
. 我们会在API的处理中调用这两个方法.
export interface User { id: number; name: string; } const users: User[] = [ { id: 1, name: "John" }, { id: 2, name: "Emily" }, { id: 3, name: "Kevin" }, ]; export const listUsers = (): User[] => { return users; }; export const addUser = (user: User): void => { users.push(user); };
HTTP服务
首先使用Deno的http标准模块来搭建一个Http服务来监听4040接口
import { serve, Server } from "https://deno.land/std/http/server.ts"; const app: Server = serve({ port: 4040 }); console.log(‘server started on localhost:4040‘)
Deno一大特点是允许我们直接通过url来导入代码模块, 当代码首次执行时, Deno会从远端的url下载代码并缓存到本地, 这样下次再运行时就无需再次下载.
deno --allow-net index.ts
--allow-net
标明程序运行时需要使用网络访问, 如果不提供该标志, 程序将由于无法访问网络而报错. 执行之后, 我们会看到输出
Download https://deno.land/std/http/server.ts server started on localhost:4040
接下来我们为Http服务添加API处理接口. Deno的Http Server
继承了
AsyncIterable接口
, 这样就允许我们使用
for await语法来异步的遍历与监听
http请求. 如果你对
AsyncIterable和for await语法不太清晰, 建议读
这篇文章.
import { serve, Server } from "https://deno.land/std/http/server.ts"; const app: Server = serve({ port: 4040 }); console.log(‘server started on localhost:4040‘);
for await (const req of app) {
switch (req.url) {
case "/user": {
switch (req.method) {
case "POST": {
const newUser = await parseRequestBody<User>(req);
addUser(newUser);
respondWithBody(req, true);
break;
}
default:
respondNotFound(req);
}
break;
}
case "/users": {
switch (req.method) {
case "GET": {
respondWithBody<User[]>(req, listUsers());
break;
}
default:
respondNotFound(req);
}
break;
}
default:
respondNotFound(req);
}
}
当一个请求进来时, 我们使用switch
来筛选req.url和req.method, 并且只处理匹配的url和method, 否则调用respondNotFound方法来返回404错误. 使用switch是最基本的分流API的方法, 当API越来越多后, 需要规划更多的中间层来分流请求.
const parseRequestBody = async <T>(req: ServerRequest): Promise<T> => { const buf = new Uint8Array(req.contentLength || 0); let bufSlice = buf; let totRead = 0; while (true) { const nread = await req.body.read(bufSlice); if (nread === null) break; totRead += nread; if (totRead >= req.contentLength!) break; bufSlice = bufSlice.subarray(nread); } const str = new TextDecoder("utf-8").decode(bufSlice); return JSON.parse(str) as T; }; const respondNotFound = (req: ServerRequest) => req.respond({ status: 404, body: "request not found!", }); const respondWithBody = <T>(req: ServerRequest, body?: T) => { const headers = new Headers(); headers.append("Content-Type", "application/json"); req.respond({ status: 200, headers, body: JSON.stringify(body), }); };
Docker镜像
创建Docker镜像时, 首先需要安装Deno运行时并设置路径. 最后使用--allow-net和--allow-read标志位来启动程序
# Dockerfile FROM ubuntu:18.04 COPY . server/ RUN apt-get update && apt-get install -y sudo; sudo apt-get update && sudo apt-get -y install curl unzip; curl -fsSL https://deno.land/x/install/install.sh | sh; WORKDIR server/ EXPOSE 4040 ENV DENO_INSTALL="/root/.deno" ENV PATH="$DENO_INSTALL/bin:$PATH" CMD deno run --allow-net --allow-read index.ts