文章目录
1、node.js路由
1. 什么是路由
- 在Web开发中,路由是一个重要的概念。它用于将URL映射到相应的处理程序或控制器,以便处理客户端的请求。
- 在 Node.js 中,路由是处理 HTTP 请求的关键部分,它决定了如何根据不同的 URL 和 HTTP 方法(如 GET、POST、PUT、DELETE 等)来分发请求。
- 路由通常用于构建 Web 应用程序,特别是 RESTful API。
- Node.js 本身并没有内置的路由机制,可以通过中间件库(如 Express)来实现。
路由通常涉及以下几个方面:
- URL 匹配:根据请求的 URL 来匹配路由规则。
- HTTP 方法匹配:根据请求的 HTTP 方法(GET、POST、PUT、DELETE 等)来匹配路由规则。
- 请求处理:一旦匹配到合适的路由规则,就调用相应的处理函数来处理请求。
2. node.js路由的工作原理
node.js路由的工作原理可以分为以下步骤
- 请求处理:当服务器接收到一个请求时,它会解析请求的 URL 和 HTTP 方法。
- 路由匹配:服务器会根据请求的 URL 和 HTTP 方法,在路由表中查找匹配的路由。
- 执行处理函数:找到匹配的路由后,服务器会执行与该路由关联的处理函数。
- 发送响应:处理函数执行完毕后,服务器会向客户端发送响应。
2、在node.js中,内置模块实现路由
1.案例
在Node.js中,可以使用内置的http模块来创建简单的服务器,并且处理路由。
const http = require('http');
const port = 3001
// 创建服务器并定义路由
const server = http.createServer((req, res) => {
const { url, method } = req;
res.statusCode = 200
res.setHead({ 'Content-Type': 'text/plain' });
res.end('Home Page');
});
server.listen(port, () => {
console.log('Server is running on http://localhost:' + port);
});
2.createServer()
createServer() 用于创建一个 Web 服务器
访问 Node.js 的 GitHub 仓库:https://github.com/nodejs/node
- 源码存于lib/http.js文件中,内部就一行代码,实例化一个 Server 类,如下图。
- Server 类的实现存于lib/_http_server.js文件中,由源码可知,http.Server 继承自 net.Server,而 net 模块可创建基于流的 TCP 和 IPC 服务器。
- http.createServer() 在实例化 net.Server 的过程中,会监听 request 和 connection 两个事件。
3.end()
很多时候写入流是不需要手动调用 end() 方法来关闭的。但如果在读取期间发生错误,那就不能关闭写入流,发生内存泄漏。
为了防止这种情况发生,可监听可读流的错误事件,手动关闭,如下所示。
readable.on('error', function(err) {
writeable.close();
});
接下来看一种网络场景,改造一下之前的示例,让可读流监听 data、end 和 error 事件,当读取完毕或出现错误时关闭可写流。
const http = require('http');
const port = 3001
var fs = require("fs");
http.createServer((req, res) => {
// 创建一个可读流
const readable = fs.createReadStream(__dirname + '/data.txt')
// 处理流事件 --> data, end, and error
// data 当有数据可读时触发。
// end 没有更多的数据可读时触发。
// error 在接收和写入过程中发生错误时触发。
// finish 所有数据已被写入到底层系统时触发。
readable.on('data', chunk => {
console.log(chunk)
res.write(chunk);
});
readable.on('end',() => {
res.end('File END');
})
readable.on('error', err => {
console.log(err)
res.end('File not found');
});
readable.on('finish', err => {
res.end('File finish');
});
}).listen(port, () => {
console.log('Server is running on http://localhost:' + port);
})
若不手动关闭,页面将一直处于加载中,在KOA
源码中,多处调用了此方法。
注意,若取消对 data 事件的监听,那么页面也会一直处于加载中,因为流一开始是静止的,只有在注册 data 事件后才会开始活动。
4.listen()
listen() 方法用于监听端口,它就是 net.Server 中的 server.listen() 方法。
ObjectSetPrototypeOf(Server.prototype, net.Server.prototype);
5.req 和 res
实例化 Server 时的 requestListener() 回调函数中有两个参数 req(请求对象) 和 res(响应对象)。
- 通过 TCP 协议传输过来的二进制数据,会被 http_parser 模块解析成符合 HTTP 协议的报文格式。
- 在将请求首部解析完毕后,会触发一个 parserOnHeadersComplete() 回调函数,在回调中会创建http.IncomingMessage 实例,也就是 req 参数。
- 而在这个回调的最后,会调用 parser.onIncoming() 方法,在这个方法中会创建 http.ServerResponse 实例,也就是 res 参数。
- 最后会触发在实例化 Server 时注册的 request 事件,并将 req 和 res 两个参数传递到 requestListener() 回调函数中。
3、请求参数
- 路径解析
可以使用 URL 对象和 querystring 模块,解析参数。
用URL对象解析参数在node全局对象一文中有介绍
- querystring 模块
querystring 模块用于处理 URL 查询字符串和 POST 请求的数据。- querystring.parse()方法解析字符串为一个对象。
const querystring = require('querystring'); const str = 'foo=bar&abc=xyz&abc=123'; const obj = querystring.parse(str); console.log(obj); // { foo: 'bar', abc: [ 'xyz', '123' ] }
- querystring.stringify() 方法序列化一个对象为一个字符串。
const querystring = require('querystring'); const obj = { foo: 'bar', abc: [ 'xyz', '123' ] }; const str = querystring.stringify(obj); console.log(str); // foo=bar&abc=xyz&abc=123
- querystring.escape() 和 querystring.unescape() 方法来对和解码URL组件。
const querystring = require('querystring'); const str = 'Hello World!'; const escapedStr = querystring.escape(str); console.log(escapedStr); // Hello%20World! const unescapedStr = querystring.unescape(escapedStr); console.log(unescapedStr); // Hello World!
- querystring.parse()方法解析字符串为一个对象。
- server.js封装
server.js文件定义了服务器的启动逻辑,并在接收到请求时调用路由函数。const http = require('http'); var { URL } = require("url"); const port = 3001 // 创建服务器并定义路由 function server(route) { const server = http.createServer((req, res) => { const { url, method } = req; const pathname = new URL(req.url, `http://${req.headers.host}`).pathname route(pathname, method); // 设置响应头和响应内容 res.writeHead(200, { 'Content-Type': 'text/plain' }); res.write('Start1 Page'); // 向客户端发送响应内容 res.end(); }); server.listen(port, () => { console.log('Server is running on http://localhost:' + port); }); } module.exports.server = server;
- router.js文件
router.js 处理路由逻辑,定义并导出 route 函数,用于在服务器收到请求时处理不同路径function route(pathname, method) { console.log("pathname:" + pathname, '请求方式:' + method); } // 导出了 route 函数 exports.route = route;
- index.js文件
index.js 是程序的入口文件,负责启动服务器并将路由模块传入服务器模块中。
现在启动应用(node ./index.js
,始终记得这个命令行),随后请求一个URL,会看到应用输出相应的信息,表明 HTTP 服务器已经在使用路由模块了,并会将请求的路径传递给路由:var server = require("./server"); var router = require("./router"); console.log(router.route) server.server(router.route);