上一篇文章 静态资源服务器
,我们通过 mime-types
,将原本繁琐的文件类型判断和 MIME
值对应处理地更为健壮,扩展性更强。也已经基本解决了各种静态资源的请求和读取问题。
那么这一章呢,我们来实现一个:接口服务器
。
回想一下,前后端功能对接,是不是后端提供接口地址,然后前端带上参数请求该地址,拿到返回的数据,并把数据渲染到页面上的呢。
要实现接口的话,其实也非常简单,可以说简单地不像话。请求接口和请求静态资源文件没有什么太大的区别。一个是读取并 返回文件
,一个则是 返回JSON数据
。
import { createServer } from 'node:http';
import { readFileSync, existsSync } from 'node:fs';
import { lookup } from 'mime-types';
const server = createServer((req, res) => {
if (req.url.startsWith('/api/') === true) {
if (req.url === '/api/getUserScore') {
res.end({
code: 0,
msg: '查询数据成功',
data: {
id: 1,
name: 'chensuiyi',
score: 100
}
});
}
} else {
if (req.url === '/') {
req.url = '/index.html';
}
if (existsSync(`./public/${req.url}`) === true) {
res.setHeader('Content-Type', lookup(req.url));
res.end(readFileSync(`./${req.url}`));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
}
});
server.listen(3000, '127.0.0.1', () => {
console.log('服务已启动,监听端口为:3000');
});
为了将资源请求和接口请求进行区分,便于我们判断。我们规定,所有的接口请求都必须以 /api/
为前缀。
凡是请求地址的前缀为 /api/
的,则进入接口相关的处理逻辑,否则就是资源相关的处理逻辑。
那么,我们也设计一个接口,请求路径是 /api/getUserScore
,功能是获取用户分数。
当我们的服务器匹配到这个请求路径的时候,就返回对应的 JSON 数据。
服务器启动后,我们在浏览器发起请求。
但是,不光浏览器没拿到数据,连我们的后端程序都崩了。
TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Object
报错内容是这样的,我们分成几段来看。
什么错误呢?TypeError
。
Type=类型
,Error=错误
,对了,是 类型错误
。
什么样的类型错误呢?ERR_INVALID_ARG_TYPE
。
ERR=错误
,INVALID=无效的
,ARG=参数
,TYPE=类型
。
连起来就是,无效的参数类型
。
哪个地方出的错呢?D:/codes/nodeFullStack/api/server.js:8:17
。
server.js
文件的 第8行
,这行有个什么函数?res.send()
。
哦,我明白了,res.send()
函数的参数类型错了。
可我们明明给的是 JSON对象
啊,这个 JSON对象
又没错。
唉,还真是这个 JSON对象
错了,咱们继续往后看。
The "chunk" argument must be of type string or an instance of Buffer or Uint8Array.
The “chunk”
就是我们要给 res.send
提供的参数,它的中文含义叫做 块
,就是一块一块的,一份一份的意思。
也就是说,我们提供给 res.send()
函数的参数,要一块一块的,一份一份的。
那么什么样的类型,是一块块,一份份的呢?
继续看后面的文字,翻译过来就是 “参数必须是字符串类型,或者是 Buffer,Uint8Array 的实例”
。
Buffer 和 Unit8Array 看不懂不要紧,一两句话也说不清,购买了本小册的话,可以私聊加我微信 chensuiyime
到问答群交流提问。
那么这个字符串类型,肯定明白吧,问题找到了,res.send()
函数需要字符串类型,而我们给它的确实 JSON对象
。
那么如何将 JSON 结构变成字符串呢,很简单,JSON.stringify
来帮忙。
if (req.url === '/api/getUserScore') {
res.end({
code: 0,
msg: '查询数据成功',
data: {
id: 1,
name: 'chensuiyi',
score: 100
}
});
}
// 以上代码,改成如下
if (req.url === '/api/getUserScore') {
res.end(
JSON.string({
code: 0,
msg: '查询数据成功',
data: {
id: 1,
name: 'chensuiyi',
score: 100
}
})
);
}
这么一操作,我们就能拿到接口的返回数据了。
但,问题又来了,怎么乱码了?
很简单,还记得前面章节,如何解决乱码问题的吗?
如果忘记了,请看第二章从一个简单的例子开始。
那么我们的解决方法如下:
if (req.url === '/api/getUserScore') {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
// res.setHeader('Content-Type', 'application/json'); 也可以不加编码,也会正常显示
res.end(
JSON.stringify({
code: 0,
msg: '查询数据成功',
data: {
id: 1,
name: 'chensuiyi',
score: 100
}
})
);
}
设置返回头的 Content-Type
属性的值为 application/json
。
这就等于告诉浏览器:嘿,听好了,我要返回的数据是 JSON 结构,用 UTF-8 编码显示,可别整乱码了
。
这么一来,数据显示就正常了。
也可以看到,我们设置的头部 Content-Type
属性也生效了。
那么到了这一步,我们的接口服务器,就算完成了。
what?这就完成了?
没错,完成了。
如果需要增加新的接口,那么增加不同的接口判断就行了。
后续章节,我们会逐步完成接口的功能,比如 读取数据库
,实现 登录注册
等等。
但是,这些都是对接口功能的完善,本质上来说,所谓的后端接口,就是判断不同的接口请求,返回不同的数据而已。我们所想象的全栈开发,接口开发好难好难,其实远远没有想象中那么难。
那么我们再回过头思考前面的一个问题,为什么接口不能返回 JSON对象
?三秒中思考一下。
OK,这是因为,JSON对象
是一个数据结构,是一个抽象的东西。
你不仅不能返回 JSON对象
,也不能返回 Array数组
。
浏览器和服务器之间的数据交换,交换的是 数据
,而不是 结构
。
我们的上一章静态资源服务器,读取各种文件并进行返回,也不是返回 结构
,而是不同文件的 数据
。
图片有图片的数据,文件有文件的数据,这是实实在在的数据,而不是抽象的结构。
所以呢,我们才需要用到 JSON.stringif
y 方法,来把 JSON对象
变成 JSON 字符串
数据。
本章非常重要,理解清楚,可以有效地解决对于全栈开发,接口开发的未知和恐惧,其实也就那样。
那么后续呢,我们进一步完善我们的接口服务器,让其更健壮,更方便,为我们即将到来的实战,个人博客全栈项目开发,打下基础。