先看上节留的作业:
怎么在不改动代码的情况下,启动时能切换端口号呢?
很容易想到解决方案:
- 读取环境变量
- 读取命令参数
- 读取配置文件
前两者也都很容易做到,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/");
这时看到控制台打印的所有接口都加上/api的前缀。再访问http://localhost:8000/就没有响应了,http://localhost:8000/api才是刚才的接口。
在生产中,这个前缀也是加在配置文件里的。鉴于我们这个项目是服务端渲染,就不加了。大家把这句去掉吧。
作业
作为后台服务,不可能所有响应都正常,肯定会有异常或错误,但打印在控制台的错误信息可能一闪而过,我们怎么才能定位错误呢?