Hono 前后端分离系列 03:路由、请求和响应应该怎么设计
接口设计不是把函数暴露出去。
它是在给前端、后端和未来的自己签一份契约。
Hono 写路由很简单:
1 | app.get('/api/users', listUsers) |
但正因为简单,很多项目会很快写乱:
1 | /getUser |
接口一旦乱了,前端调用会痛,后端维护也会痛。
这篇文章不只讲 Hono 的路由语法,更重要的是讲前后端分离项目里,路由、请求和响应应该怎样设计。
一、先从资源开始想
REST API 的核心不是动词,而是资源。
比如用户、文章、订单、文件:
1 | users |
不要一上来写:
1 | /getUserList |
更推荐:
1 | GET /api/users |
Hono 里可以这样写:
1 | import { Hono } from 'hono' |
再挂到主应用:
1 | app.route('/api/users', users) |
这样路由会自然长成一棵树,而不是一堆散乱字符串。
二、请求参数分三类
前端调用接口时,参数一般分三类。
第一类是路径参数:
1 | /api/users/:id |
用来表示资源身份:
1 | const id = c.req.param('id') |
第二类是查询参数:
1 | /api/users?page=1&pageSize=20&keyword=tom |
用来表示筛选、排序、分页:
1 | const page = c.req.query('page') |
第三类是 body:
1 | { |
用来提交复杂数据:
1 | const body = await c.req.json() |
不要把所有参数都塞进 query,也不要把资源 id 放进 body。规则简单一点,前后端都省心。
三、响应结构要统一
前端最怕的响应不是错误,而是不一致。
有的接口返回:
1 | { "data": [] } |
有的返回:
1 | { "list": [] } |
有的报错:
1 | { "message": "failed" } |
有的报错:
1 | { "error": "failed" } |
这会逼前端在每个接口里写特殊处理。
我更推荐统一成两类。
成功响应:
1 | { |
失败响应:
1 | { |
Hono handler 可以这样写:
1 | app.get('/api/users/:id', async (c) => { |
注意状态码不要偷懒。
业务失败不应该永远返回 200。
四、状态码要有基本纪律
前后端分离项目里,常用状态码其实不多。
1 | 200 请求成功 |
不要把所有错误都塞进 500。
也不要所有失败都返回 200,然后靠 code 判断。
HTTP 状态码负责第一层语义,业务 error.code 负责第二层语义。两个一起用,前端处理会清楚很多。
五、路由文件不要写业务细节
一个常见坏味道是 route 里什么都做:
1 | app.post('/api/users', async (c) => { |
刚开始没问题,后面会变成 300 行 handler。
更好的方式是让 route 只做 HTTP 层:
1 | users.post('/', async (c) => { |
业务逻辑放到 service:
1 | export const userService = { |
前后端分离项目长期维护的关键,不是路由能不能写,而是路由能不能保持薄。
六、分页接口要提前统一
列表接口一定会越来越多。
所以分页结构最好一开始就统一:
1 | GET /api/users?page=1&pageSize=20 |
响应:
1 | { |
不要一个接口叫 list,一个接口叫 rows,一个接口叫 records。
前端表格组件、分页组件、请求封装都会因此变复杂。
七、最后的建议
Hono 给你的是轻量路由能力,不是接口设计能力。
接口设计要靠团队纪律。
我的基本规则是:
1 | 用资源名设计路径 |
这套规则不花哨,但很管用。
前后端分离不是前端和后端各写各的。真正的连接点就是 API。API 一乱,整个项目都会变慢。