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

第二章 收信、回信

读短信

收到前端发来的请求时,后端首先要做的,就是读请求:

读取请求
1
const http = require('http')
2
3
const server = http.createServer((req, res) => {
4
console.log('收到请求', new Date())
5
const method = req.method // 请求的 method
6
const url_str = req.url // 请求的 url,字符串类型,格式大致为 /user?name=PPz&year=3
7
console.log(method, url_str, req.headers) // headers 里有很多默认参数
8
9
res.write('Hello, Node.js')
10
res.end()
11
})
12
13
server.listen(8080)
14
console.log('ok')

不用解释吧,js 写后端就是这么简单直接。

我想你已经注意到了,上面的 reqres 对象分别代表 request(请求)和 response(响应)。 如果你想读取前端发来的数据,就去 req 里找;想返回数据给前端,就使用 res

读路径里的参数

前端向后端发消息,一种重要的载体,就是路径:把参数放路径里。 像 /user?name=PPz&year=3,这个路径里就包含了 nameyear 参数。
Node.js,这样读:

读取路径参数
1
const http = require('http')
2
3
const server = http.createServer((req, res) => {
4
console.log('收到请求', new Date())
5
const method = req.method
6
const url_str = req.url // 请求的 url,字符串类型,格式大致为 /user?name=PPz&year=3
7
console.log(method, url_str, req.headers)
8
9
const url = new URL(url_str, 'http://' + req.headers.host)
10
const search_params = url.searchParams
11
console.log(search_params.get('name')) // 'PPz'
12
console.log(search_params.get('year')) // '3' 参数值都是字符串
13
console.log(search_params.get('unreal')) // null 不存在的参数值为 null
14
15
res.write('Hello, Node.js')
16
res.end()
17
})
18
19
server.listen(8080)
20
console.log('ok')

注意点 一

在浏览器环境(前端环境)里,也有 URL 这个全局构造器。 不太熟悉的同学,要查一查、搜一搜了。 Node.js 里的 URL 和浏览器里的 URL 是一样的。
其中的 url.searchParams 是一个 URLSearchParams 对象,浏览器环境里当然也有。

注意点 二

下面两种代码,发出的请求是一样的(所以解析方法也一样):

1
// 代码一
2
axios.get('/user', {
3
params: {
4
name: 'PPz',
5
year: 3,
6
}
7
})
8
9
// 代码二
10
axios.get('/user?name=PPz&year=3')

注意点 三

很多时候,前端上传的数据,不仅量大,而且结构复杂。比如:

1
axios.post('/user', {
2
name: 'PPz',
3
year: 3,
4
friends: [
5
{ name: 'CCz', year: 2 },
6
{ name: 'YYz', year: 2 },
7
]
8
})

这种情况下,数据通常会以“附件”的形式,发给后端。解析 “附件” 比较复杂,这会在下下一章讨论。

回短信

要注意:“回”短信,是回复。在 http 通信中,后端只能回复。 没有请求时,后端就在那等着,啥也不干。 就像前端里,添加点击事件后,只要用户不点按钮,监听器里的代码永远不会执行。

扩展阅读(可以跳过)    websocket 可以实现后端“主动”发消息。 但前提是,前端要事先主动发起连接、建立连接,这之后,后端才能“主动”发消息。 websocket 不在此小教程讨论范围内。

之前的例子中,都回复一个字符串。当然也可以回复 json:

回复 json
1
const http = require('http')
2
3
const server = http.createServer((req, res) => {
10 collapsed lines
4
console.log('收到请求', new Date())
5
const method = req.method
6
const url_str = req.url // 请求的 url,字符串类型,格式大致为 /user?name=PPz&year=3
7
console.log(method, url_str, req.headers)
8
9
const url = new URL(url_str, 'http://' + req.headers.host)
10
const search_params = url.searchParams
11
console.log(search_params.get('name')) // 'PPz'
12
console.log(search_params.get('year')) // '3' 参数值都是字符串
13
console.log(search_params.get('unreal')) // null 不存在的参数值为 null
14
15
// res.write('Hello, Node.js')
16
res.setHeader('Content-Type', 'application/json')
17
const user = {
18
name: 'PPz',
19
year: 3,
20
adult: false,
21
}
22
res.write(JSON.stringify(user))
23
res.end()
24
})
25
26
server.listen(8080)
27
console.log('ok')

可以看到,第 22 行,依然是回复一个字符串。 只不过,是把一个对象,先转化成 json 格式的字符串,然后响应。 实际上,其他编程语言、后端框架,在响应 json 时,也是一样的,并不存在什么“高级的东西”。

而第 16 行,是告诉客户端(前端):响应数据的格式(即 “content(内容) type(类型)是 json”)。
这里看起来有点奇怪:一般情况下,后端的同事,都是口头或文档告诉前端 “响应的格式是什么”,为什么要写在代码里? 这是因为,像 aioxs 这样库,在收到后端的回信(响应)后,会自动读取 header 里的 Content-Type 字段,如果发现是 application/json, 就会帮我们解析好(用 JSON.parse)。
但如果前端直接用浏览器内置的 fetch api,因为 fetch 没有自动解析的功能, 所以 Content-Type 就没必要设置了。

可以用 Postman 试一下,设不设置 Content-Type 有什么区别。(注意:改了代码后,记得重启项目)

小结

如果一口气看了三章,那么建议动手敲一敲代码、探索探索,然后休息休息。

应了解:

练习查询文档

以下内容,可以跳过

就我个人而言,一直使用的学习方法是(分两步): 第一步:先了解概念、流程,大致了解学的东西是什么、有哪些操作。 相比于“大教程”,这只占用很少的时间。

但细节,在“小教程”里是学不到的。 但是的但是,在“大教程”里真能学到“细节”? 虽然大教程里,会介绍细节,但细节之所以称为细节,就是因为细节多、难记。 除非你经常使用,否则,仅在教程里看一眼、听一句,其实是过目就忘。

所以,我学习时,第二步,也就是“了解大概”之后, 会去尝试使用它,遇到问题就查文档,或直接用搜索引擎。

比如 reqres 对象的官方文档:

文档里的内容非常非常多,我们只需要根据目录,找我们想知道的就可以了。

上一章    Hello, Node.js 下一章    路由与异常