---
title: "后端交互"
date: "2026-02"
category: "随笔"
tags: ["随笔"]
description: "我知道洋葱模型：请求全部进来之后，出去时也会从这里走一段。 中间件的话，用我的理解，有点像是 Python 里面的 @ 装饰器。在所有函数被调用之前都会先走一遍这里..."
source: "https://enriquemark.com/zh-hans/posts/%E5%90%8E%E7%AB%AF%E4%BA%A4%E4%BA%92"
---

我知道洋葱模型：请求全部进来之后，出去时也会从这里走一段。

中间件的话，用我的理解，有点像是 Python 里面的 @ 装饰器。在所有函数被调用之前都会先走一遍这里。如果需要对每一个路由访问都进行一次的重复操作，那这些操作基本上都要放到中间件里。

### 关于 Token
一般称为 JWT。这东西主要是给后端用的，比如鉴权。你不能前端每次发请求过来都去查一遍数据库，那样效率太低也很麻烦，IO耗损会很大。

所以会有 Token 机制：
1. 前端每次请求都会把 Token 发过来，后端通过它核查登录状态。
2. 如果验证没有问题，用户就不会被跳转到登录界面。
3. Token 中通常包含一些基本信息，比如：
   (a) 用户 ID
   (b) 权限信息
   (c) Token 的过期时间

一般来说，对 Token 的校验都会放在中间件里，每次在路由跳转之前先校验权限，确认用户是否有权进入页面。而另一个东西就是 JSON 的返回数据，这是给前端用的（除非要求前端每次都很麻烦地去解析 Token）。前端会借助里面的数据，来决定是否要进行下一步渲染等等。

### 关于密码的处理
最好是在后端将其转换为哈希（Hash），否则会面临很尴尬的安全问题。如果哈希是在前端生成的，攻击者只需要修改一下 JS 代码，就可以直接绕过验证逻辑与数据库交互并完成登录，这会让加密行为变得根本没意义。所以各种安全验证最好都放在后端（服务端）来处理，因为后端运行在服务器上，即便有人在浏览器前端去下功夫破解，也没办法更改后端的逻辑。而后端对前端是基于一个零信任原则的。

### 关于 JWT Token 在前端的存储
通常有两个选择：直接存到 LocalStorage 里面、把它封装成 Cookie。与此对应，后端也需要进行相应处理。之所以将 Cookie 单独划分出来，主要原因是 Cookie 的安全系数更高。

如果只是把 Token 存在 LocalStorage 中，只要 JS 一行代码就能把它全部读取出来。在这种情况下，它就没办法防范 XSS 攻击，只要有一个网页端的 JS 代码注入，就能把所有的 Token 扒得一干二净，而 Cookie 就可以有效避免这个问题。

而且这里有一个重要的区分：F12 调试台确实可以直接获得 Cookie，但那是我作为浏览器使用者的的最高权限；而另一个则是直接在网页端运行的 JS 代码——这两者是完全不一样的。对于网页端运行的 JS 代码来说，设置了 HttpOnly 的 Cookie 它就是拿不到的，但是对于 LocalStorage 讲，网页端运行的 JS 也可以直接拿到，这个差别很大。

### 关于路由地址
这个地方遵循相对路径的逻辑（包括主路由、子路由）。

比如主路由是 /api，那么所有 /api 的请求都会被转发到这里，然后再根据后面的依赖关系转发到子路由。例如 /api/xx 就会被转发到 /xx。

关于子路由的写法：如果要写子路由的中间件，且该中间件已经挂载到子路由上了，路径直接写一个\*即可，在子路由的模块里面，单独写其相对地址：

```ts
// dashboard.ts
const dashboardRoute = new Hono()

// 1. 保护这个子路由下的“所有”路径
dashboardRoute.use('/*', jwtMiddleware) 

// 2. 定义具体路径 (注意这里不用写 /dashboard 了，只写相对路径)
dashboardRoute.get('/profile', (c) => c.text('我的资料'))
dashboardRoute.get('/settings', (c) => c.text('设置'))
```

关于这个星\*的使用其实挺有意思的：如果是 \/* 的话，那一般就是类似通配符的存在，在这个路径后面的内容会全部匹配；但如果只有一个星号的话，那指的就是这个路由里面的全部匹配。这里的坑在于，子路由这里一定要带 /\*。

然后主路由如果只有一个星号的话，那就是主路由之下的全局匹配。如果是在挂载路由组的时候，那一定要把子路由的路径完整写下来，这样才能挂到主路由上，防止错配：

```ts
// app 是主路由，接子路由的完整路径
app.route('/api', authRoute)
app.route('/api/dashboard', dashboardRoute)
```

### 关于接口生成和调试
这方面有比较好的库可以一次性生成文档。接口搞好后，让AI写好注释，然后再接上Swagger这样的工具里，就可以自动产出接口文档了。而且可以在线调试，自动更新，非常好用。
当然还有另一个选择，就是一步到位，让 AI 全部处理。确实，这个工具在今天看来是可有可无的，但如果有一些调试上的需求，加上它可能会比较好。或者说，甚至连调试的单元测试也可以交给 AI 来写，这确实也没问题。
