给前端程序员的后端小教程

第三章 路由与异常

路由?是什么

作为前端程序员,只要用过 Vue 或 React,那不会没见过 route、routes、router。

一个 route 是一组对应关系,比如 /user 对应 “用户管理” 页面。 下面是一个常见的 route:

1
{
2
path: '/user',
3
component: User, // User 是一个 UI 组件
4
}

解释为:当浏览器地址栏的路径是 /user 时,渲染 User 组件。 多个 route 变成一个 route 数组,也就是 routes。

routes 里只包含 “对应关系”,并不会监听地址栏变化、再渲染 xx 组件。 这个功能由 router 承担。

可参考 Vue Router 官方文档的这段代码,注意里面的变量命名:

1
import { createMemoryHistory, createRouter } from 'vue-router'
2
3
import HomeView from './HomeView.vue'
4
import AboutView from './AboutView.vue'
5
6
const routes = [
7
{ path: '/', component: HomeView },
8
{ path: '/about', component: AboutView },
9
]
10
11
const router = createRouter({
12
history: createMemoryHistory(),
13
routes, // router 里有 routes
14
})

后端的 route 和 router 在概念、使用场景上,和前端是一样的。

handler?是什么

还有一个概念很重要 —— handler,它像前端的监听器:

后端路由

作为前端程序员,肯定知道怎么区分请求,比如:

路径相同时,未必是相同的请求。 还要看 method,甚至要看看参数。

后端 route
1
const http = require('http')
2
3
const routes = [
4
{ method: 'GET', path: '/user', handle: get_user },
5
{ method: 'DELETE', path: '/user', handle: delete_user },
6
// ...
7
]
8
9
const server = http.createServer((req, res) => {
10
console.log('收到请求', new Date())
11
const method = req.method
12
const url = new URL(req.url, 'http://' + req.headers.host)
13
14
const route = routes.find(
15
route => route.method == method && route.path == url.pathname
16
)
17
18
route.handle(req, res) // 这里先不考虑 handle 不存在的情况
19
})
20
21
server.listen(8080)
22
console.log('ok')
23
24
// 封装“响应 json”操作
25
function respond_json(res, data) {
26
res.setHeader('Content-Type', 'application/json')
27
res.write(JSON.stringify(data))
28
res.end()
29
}
30
31
// 用于处理“获取用户”请求
32
function get_user(req, res) {
33
const users = [ // 真实的后端程序里,这一步的数据要从数据库查询
34
{ name: 'PPz', year: 3 },
35
{ name: 'CCz', year: 2 },
36
]
37
respond_json(res, users)
38
}
39
40
// 用于处理“删除用户”请求
41
function delete_user(req, res) {
42
// 删除用户
43
// 假装已经删除了
44
res.write('ok') // 正式的后端不会只返回 'ok',但本章先只关注 route 和 router
45
res.end()
46
}

上面第 3-7 行,是 routes。 即 “xx 路径、yy method” 的请求由哪个函数来处理。

第 14-16 行,实际是起到一个 router 的作用, 正经项目中的 router 尽管比这个复杂,但原理跟这两三行代码一样。

第 18 行,从 “简易 router” 那里得到 handle 后, 执行 handle,并把 req 和 res 对象传过去。

第 24-29 行,封装了一个 “用于响应 json” 的函数。

第 31-38 行,处理 GET /user 请求。

第 40-46 行,处理 DELETE /user 请求。

404 和 500

上面的案例中,为展示最简单的路由,未考虑 “handle 不存在” 和 “handle 执行异常” 这两种情况。

404 和 500
1
const http = require('http')
2
3
const routes = [
4
{ method: 'GET', path: '/user', handle: get_user },
5
{ method: 'DELETE', path: '/user', handle: delete_user },
6
{ method: 'POST', path: '/user', handle: create_user },
7
// ...
8
]
9
10
const server = http.createServer(async (req, res) => {
11
console.log('收到请求', new Date())
12
const method = req.method
13
const url = new URL(req.url, 'http://' + req.headers.host)
14
15
const route = routes.find(
16
route => route.method == method && route.path == url.pathname
17
)
18
19
if (!route.handle) { // handle 不存在时
20
res.writeHead(404)
21
res.write('未找到资源')
22
res.end()
23
return
24
}
25
26
try {
27
// route.handle(req, res)
28
await route.handle(req, res) // 大部分情况下,handle 都是异步的
29
} catch (err) { // handle 发生异常时
30
console.error(`处理请求 ${method} ${url.pathname} 时,发生异常`)
31
console.error(err)
32
res.writeHead(500)
33
res.write('服务器内部错误')
34
res.end()
35
return
36
}
37
})
27 collapsed lines
38
39
server.listen(8080)
40
console.log('ok')
41
42
// 封装“响应 json”操作
43
function respond_json(res, data) {
44
res.setHeader('Content-Type', 'application/json')
45
res.write(JSON.stringify(data))
46
res.end()
47
}
48
49
// 用于处理“获取用户”请求
50
function get_user(req, res) {
51
const users = [ // 真实的后端程序里,这一步的数据要从数据库查询
52
{ name: 'PPz', year: 3 },
53
{ name: 'CCz', year: 2 },
54
]
55
respond_json(res, users)
56
}
57
58
// 用于处理 “删除用户” 请求
59
function delete_user(req, res) {
60
// 删除用户
61
// 假装已经删除了
62
res.write('ok')
63
res.end()
64
}
65
66
// 未实现的 “创建用户”
67
function create_user(req, res) {
68
throw Error('现在还不能创建用户')
69
}

404、500 这种码,前端程序员也不陌生,你一定也见过 200。 这种三位数字的码叫 http 状态码,一般地:

但 http 状态码并不常用, 因为它通常只用于表示通信过程的状态。 换句话说,当出现 4xx 或 5xx 时, 不是因为用户少填了字段或什么, 而是前端或后端的代码出现了 bug。

而且后端业务很复杂时,我想,500-599 这 100 个码可能不够用。 另外,http 状态码不常用还有历史原因: 互联网早期,后端响应的状态码不是 200 时,会被网络提供商拦截……

前端程序员对下面的数据结构一定不陌生:

1
{
2
code: 0,
3
msg: 'success',
4
data: {
5
// ...
6
},
7
}

这里面的 code 和 msg 其实是 json 数据的一部分:

1
res.write(JSON.stringify({
2
code: 0,
3
msg: 'success',
4
data: {
5
// ...
6
},
7
}))

code 为 0 代表正确,非 0 代表错误,这在我头一回见时,觉得很奇怪: 因为往往 0 代表 “假”、“错误”。 但我细想,成功只有一种,错误却可能千奇百怪:用户少填字段、格式不正确……

所以用 0 代表正确,用非 0 们代表各种各样的异常。

或者,你把 code 当成 “错误码” 也行:那么 0 就是 “没有错误”。

上一章    收信、回信 下一章    请求的“附件”