跳转至内容
There is a version suitable for your browser's language settings. Would you like to go to the english language of the site?
主页文档

起步

H1

所有的 Milkio 工程都配备了一个专属的客户端。利用 TypeScript 开发的其他工程可以轻松地通过这个客户端调用你的 Milkio 工程,享受完善的类型检查、自动补全,以及类型即是文档的便利。

const result = await client.execute("hello-world/say", { params: "🥛 Milkio" });

让我们开始吧!

假设你已经拥有一个想要调用 Milkio 的工程,无论它是 Vue、React、Electron、React Native、Mini Program,还是其他任何工程,甚至是另一个后端工程,只要它们支持 TypeScript 语言。如果你还没有,我们建议你尝试使用 Vite 来启动你的工程。

准备就绪后,我们设想你的工程和 Milkio 工程位于同一个目录下,目录结构大致如下:

终端窗口
workspace
├─ a-milkio
├─ ...
└─ package.json
└─ a-web-app
├─ ...
└─ package.json

现在,打开你的 web 工程所在的 VS Code,通过点击 文件 -> 将文件夹添加到工作区.. 将你的 Milkio 工程也加入进来。这样,你就可以在 VS Code 中同时处理两个工程了。

在 VS Code 中,选择 终端 -> 新建终端,切换到你的 web 工程目录下,并在终端中安装你 Milkio 工程中的客户端包:

终端窗口
bun i ../a-milkio/packages/client

在你的 web 工程中,选择一个你喜欢的位置,创建一个 client.ts 文件,并写入以下代码:

import { createClient } from "client";
export const client = createClient({
baseUrl: "http://localhost:9000/"
});

现在,让我们通过客户端尝试执行下你的 Milkio 工程吧!UwU

const result = await client.execute("hello-world/say", { params: "🥛 Milkio" });
console.log(result);

失败处理

由于网络连接并不总是稳定,请求也不总是顺利,因此我们必须考虑如何处理请求失败的情况。

对于失败的请求,Milkio 会通过返回值而不是抛出异常来通知你失败的原因(就像 Go 语言一样)。抛出异常会让我们不自觉地忽略异常处理,导致代码在不可预料的地方停止运行。我们应该在请求失败时,向用户展示失败的原因(比如显示一个提示框),并恢复一些变量的设置。

const result = await client.execute("hello-world/say", { params: "🥛 Milkio" });
if (!result.success) return alert(result.fail.message);
console.log("你成功地说出来了:", result.data);

Milkio 通过联合类型,确保你必须在确认请求成功后,才能正确提取 result.data,就像上面的代码一样。这有助于保持我们应用的准确性,即使我们的请求总是成功的,用户的网络连接也可能并不总是良好。这与我们在 Go 中的常见做法相似:

// golang
if err != nil { return err }
fmt.Println("Hello, World!")

用户友好的失败消息

失败消息旨在解释失败的原因,但并非所有的消息都足够友好。例如,如果用户输入的密码过短,导致 TYPE_SAFE_ERROR 失败,我们可能不会希望在界面上显示:

Parameter Error: The current value is '$input.user.password', which does not meet 'string & tags.MinLength<8>' requirements

而是希望用户看到更友好的提示:

密码太短啦,试试加长到 8 个字符以上吧

Milkio 在失败时,除了返回消息,还会返回与此次失败有关的数据,即 Milkio 工程的 /src/fail-code.ts 中每个方法的第一个参数。

我们可以利用这些数据,进行更细致的失败处理。例如:

const result = await client.execute(...);
if (!result.success) {
if (result.fail.code === "TYPE_SAFE_ERROR" && result.fail.data.path === "$input.password") return alert("密码太长或者太短");
if (result.fail.code === "TYPE_SAFE_ERROR" && result.fail.data.path === "$input.username") return alert("用户名不符合规范,需要不包含汉字和符号");
return alert(result.fail.message); // 不想自定义类型安全错误以外的消息
}

对于 TYPE_SAFE_ERROR,你可以获取以下信息进行判断:

名称用途示例
path类型匹配失败的路径,总是以 $input 开头$input.password
expected期望的类型string & tags.MinLength<8>
value实际接收到的值123456

如果你需要访问提供流作为结果的 API,你应该使用 executeStream 方法。Stream 章节将详细介绍。

非浏览器环境使用

我们假设你的代码在浏览器中运行,因此它使用了 localStoragefetch 以及 AbortController。如果你希望在其他服务器环境中运行(比如,我们可能希望在一个 Milkio 中调用另一个 Milkio),确保你的环境中提供了这些功能。

通常需要定制的是 localStorage,因为在服务器端,我们可能希望数据存储在数据库或其他地方。我们可以通过传递一个 storage 选项来自定义它:

export const client = createClient({
baseUrl: "http://localhost:9000",
storage: {
async getItem(key: string) {
// ...
},
async setItem(key, value) {
// ...
},
async removeItem(key) {
// ...
}
}
});

storage 中的方法可以是异步的,这样可以方便你与数据库等数据存储解决方案结合使用。

如果你的代码运行环境没有 fetchAbortController(对于 Bun、Cloudflare Worker 等环境是支持的),你可以通过传递 fetchAbortController 选项来自定义它们:

export const client = createClient({
baseUrl: "http://localhost:9000",
fetch: fetchPolyfill,
abort: abortControllerPolyfill,
});

如果你不使用 Stream,你可以不提供 abort 选项,仅提供 fetch 即可。