初探Deno: 配合Docker搭建一个简单的API服务

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 /usersPOST /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

 

 

初探Deno: 配合Docker搭建一个简单的API服务

上一篇:Winform程序及dll打包成一个可执行的exe


下一篇:C# 7-zip 压缩和解压缩