1.12 配置文件

84 阅读2分钟

先看上节留的作业:

怎么在不改动代码的情况下,启动时能切换端口号呢?

很容易想到解决方案:

  1. 读取环境变量
  2. 读取命令参数
  3. 读取配置文件

前两者也都很容易做到,Deno都提供了相关的API,比如读取环境变量只需要修改下src/main.ts中代码:

const port = Number(Deno.env.get("PORT") || 8000);
await app.listen({ port });

配置文件也不复杂,这里为什么推荐使用配置文件呢?因为环境变量和命令参数如果只有几个还好,太多的话也难以维护。而配置文件就没有这个顾虑,在实际生产中需要配置的属性会非常多。

配置文件的类型有各式各样,最常见的是json文件和yaml文件,而json文件的问题在于格式过于严苛,我们推荐使用yaml格式。

配置端口

config.yaml

新建config.yaml文件:

port: 8000

import_map.json

修改import_map.json,新增2条:

"yaml_loader": "https://deno.land/x/yaml_loader@v0.1.0/mod.ts",
"jsonc": "https://deno.land/x/jsonc@1/main.ts"

utils.ts

新建src/tools/utils.ts,提供一个读取yaml文件的函数。

import { YamlLoader } from "yaml_loader";

const yamlLoader = new YamlLoader();

export async function readYaml<T>(
  path: string,
): Promise<T> {
  let allPath = path;
  if (!/\.(yaml|yml)$/.test(path)) {
    allPath += ".yaml";
  }
  const data = await yamlLoader.parseFile(allPath);
  return data as T;
}

globals.ts

新建src/globals.ts

import { readYaml } from "./tools/utils.ts";
import { parse } from "jsonc";

export type Scripts = {
  version: string;
};

export interface Config {
  port: number;
  version: string;
}

async function getVersion(): Promise<string> {
  const text = await Deno.readTextFile("deno.jsonc");
  const json: Scripts = parse(text);
  return json.version;
}

const config = await readYaml<Config>("config.yaml").catch(() => {
  console.error("not read config.yaml");
  Deno.exit(1);
);

config.version = await getVersion();

export default config;

main.ts

修改src/main.ts

import globals from "./globals.ts";

// 其它代码
logger.info(
  `app start with version ${globals.version}: http://localhost:${port}`,
);
await app.listen({ port: globals.port });

验证

修改config.yaml文件,重启服务,看是否端口号变了?

打印版本

对于生产环境,版本号是非常重要的。为什么呢?你每次服务上线后,版本号应该是最直观的变化,如果你没有更改版本号,你怎么验证你的服务上线成功了,不是运行的是旧版本的代码?你可能有特定的业务可以判断,但那就太复杂了。

我们修改下app.controller.ts:

import { Controller, Get } from "oak_nest";
import globals from "./globals.ts";

@Controller("")
export class AppController {
  @Get("/")
  version() {
    return `<html><h2>${globals.version}</h2></html>`;
  }
}

这样访问http://localhost:8000/时在页面上就能看到deno.jsonc文件中版本号了。

每次服务上线前,都使用tag命令来修改版本、创建git标签,这样上线后你很容易在浏览器验证是否成功。

全局API前缀

这里再提一点,如果我们提供的是纯粹的API服务,最好加一个统一的前缀,方便前端代理转发,通常这个前缀是/api。

比如在src/main.ts中新增一句:

const app = await NestFactory.create(AppModule);
app.setGlobalPrefix("/api/");

image.png

这时看到控制台打印的所有接口都加上/api的前缀。再访问http://localhost:8000/就没有响应了,http://localhost:8000/api才是刚才的接口。

在生产中,这个前缀也是加在配置文件里的。鉴于我们这个项目是服务端渲染,就不加了。大家把这句去掉吧。

作业

作为后台服务,不可能所有响应都正常,肯定会有异常或错误,但打印在控制台的错误信息可能一闪而过,我们怎么才能定位错误呢?