Node.js服务器开发(下)
Posted 橘猫吃不胖~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js服务器开发(下)相关的知识,希望对你有一定的参考价值。
Node.js服务器开发(下)
1 HTTP请求与响应处理
1.1 GET请求参数
GET参数:浏览器地址栏或页面的超链接,被放置在浏览器地址栏中进行传输,示例URL地址如下。
在该URL地址中,地址和请求参数之间用“?”分隔,参数和参数之间用“&”分隔。
服务器端接收Get请求的参数,可以调用url.parse方法对req.url进行解析,从query中获取请求参数,示例代码如下:
// 引用系统模块
const http = require("http");
const url = require("url");
// 创建服务器对象
const app = http.createServer();
// 给服务器注册监听事件
app.on("request", (req, res) =>
console.log(url.parse(req.url, true));
)
// 监听3000端口
app.listen(3000);
console.log("服务器已启动,监听3000端口,请访问localhost:3000");
在浏览器中输入localhost:3000?name=zhangsan&age=12,终端会显示以下信息:
从上面的结果可以看出Url对象中的query已经帮助我们解析出来了参数,因此我们可以从query中获取请求参数,这样就方便处理了。
示例代码:在服务器端获取请求参数
// 引用系统模块
const http = require("http");
const url = require("url");
// 创建服务器对象
const app = http.createServer();
// 给服务器注册监听事件
app.on("request", (req, res) =>
// 使用解构赋值将query获取出来
let query = url.parse(req.url, true);
console.log("请求参数1:", query["name"]); //两种获取方法
console.log("请求参数2:", query.age);
console.log(query);
res.end();
)
// 监听3000端口
app.listen(3000);
console.log("服务器已启动,监听3000端口,请访问localhost:3000");
启动服务器,在浏览器中输入:http://localhost:3000/?name=zhangsan&age=12,终端输出以下结果:
上面示例中undefined是在获取浏览器中的图标。
1.2 POST请求参数
Post请求参数被放在post请求体中进行传输。在客户端对post请求参数进行封装(方法之一放在form表单中),然后在服务器端获取post参数:导入querystring模块,将post请求体解析成对象格式
示例:从客户端发送POST请求参数到服务器端,编写form.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Post请求参数</title>
</head>
<body>
<form action="http://localhost:3000" method="post">
<label>用户名:
<input type="text" name="username"/>
</label>
<br/><br/>
<label>密码:
<input type="password" name="pwd"/>
</label>
<br/><br/>
<button type="submit">提交</button>
</form>
</body>
</html>
紧接着编写post.js文件:
const http = require("http");
const querystring = require("querystring");
const postServer = http.createServer();
postServer.on("request", (req, res) =>
let postStr = "";
// 当接收到数据时
req.on("data", (params) =>
postStr += params; // 接收一个参数拼接一个参数
// 当请求结束之后对postStr进行解析
req.on("end", () =>
let postParams = querystring.parse(postStr);
console.log(postParams);
console.log("姓名:", postParams.username);
console.log("密码:", postParams["pwd"]);
)
)
res.end("OK");
)
postServer.listen(3000);
启动服务器,在刚刚编写的Html页面中输入用户名:zhangsan,密码:123,点击提交,页面进行了跳转:
同时在终端也将信息解析出来了:
1.3 路由
用户在浏览器地址栏中输入不同的请求地址,服务器端会为客户端响应不同的内容。
例如,客户端访问“http://localhost:3000/index”这个请求地址,服务器端要为客户端响应首页的内容,这是由网站应用中的路由实现的。
路由是指客户端请求地址与服务器端程序代码的对应关系。
示例:使用路由根据不同的客户端请求地址响应不同的内容
// 引用系统模块
const http = require("http");
const url = require("url");
// 创建服务器对象
const app = http.createServer();
// 为服务器对象添加请求事件
app.on("request", (req, res) =>
// 获取客户端请求方式
const method = req.method;
// 获取请求地址
const pathname = url.parse(req.url, true).pathname;
// 设置响应头信息
res.writeHead(200,
"content-type": "text/html;charset=utf8"
)
// 实现路由功能
if (method == "GET")
if (pathname == "/" || pathname == "/index")
res.end("欢迎来到首页");
else
res.end("页面不存在");
else if (method == "POST")
res.end("您正在访问POST请求页面");
)
// 监听300端口
app.listen(3000);
启动服务器,在浏览器中输入localhost:3000,显示“欢迎来到首页”。
或者输入localhost:3000/index,也是一样的结果:
1.4 静态资源访问
静态资源服务是指客户端向服务器端请求的资源,服务器端不需要处理,可以直接响应给客户端的资源。静态资源主要包括CSS、javascript、image文件,以及HTML文件。
动态资源指的是相同的请求地址可以传递不同的请求参数,得到不同的响应资源,这种资源称为动态资源。
静态资源是存放在本地的,只能自己可以访问到,其他人不能访问。如果希望服务器端的静态资源能够被用户访问到,这就需要实现静态资源访问功能。
在服务器端创建一个专门的目录,存放静态资源。当客户端请求某个静态资源文件时,服务器端将这个静态资源响应给客户端。
示例:如何实现静态资源的访问
第一步:在当前目录下面创建public目录,用于存放静态文件
第二步:创建app.js文件,文件代码如下
// 引用系统模块http
const http = require("http");
// 创建服务器对象
const app = http.createServer();
const url = require("url"); // 引用url地址模块
const path = require("path"); // 引用系统模块path,用于读取文件前拼接路径
const fs = require("fs"); // 引用系统模块fs,用于读取静态资源
const mime = require("mime"); // 引用第三方模块
// 为服务器对象添加请求事件
app.on("request", (req, res) =>
// 处理请求,获取用户请求路径
let pathname = url.parse(req.url, true).pathname;
// 判断当前路径是否为:/,如果是路径换成/default.html,如果不是不做处理
pathname = pathname == "/" ? "/default.html" : pathname;
// 将用户的请求路径转换为实际的服务器硬盘路径
// __dirname:当前正在执行的代码的目录名(路径)
// path.join():将多个参数字符串合并成一个路径字符串
let realPath = path.join(__dirname, "public" + pathname);
// 利用mime模块根据路径返回资源的类型
let type = mime.getType(realPath);
// 读取文件
// readFile("filename",fn):用于异步读取数据
fs.readFile(realPath, ((err, data) =>
if (err) throw err; // 文件读取失败抛异常
res.writeHead(200,
"content-type": type
)
res.end(data);
))
)
// 监听3000端口
app.listen(3000);
第三步:在public文件夹下创建default.html,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
用户名:<input type="text" name="username">
密码:<input type="text" name="pwd">
<button type="submit">提交</button>
</form>
</body>
</html>
启动服务器,打开浏览器输入:localhost:3000,浏览器就会跳出来以下页面:
2 Node.js异步编程
2.1 同步异步API的概念
同步API指只有当前API执行完成后,才能继续执行下一个API。
举例:到餐馆点餐时,一个指定的服务员被分配给你服务,当你点完餐时,服务员将订单送到厨房并在厨房等待厨师制作菜肴,当厨师将菜肴烹饪完成后,服务员将菜肴送到你的面前,服务完成,此时这个服务员才能服务另外的客人。也就是说,一个服务员同时只能服务于一个客人。
同步API的执行方式:代码从上到下一行一行执行,下一行的代码必须等待上一行代码执行完成以后才能执行。
console.log("before");
console.log("after");
异步API指当前API的执行不会阻塞后续代码的执行。
举例:到餐馆点餐时,在点餐后服务员将你的订单送到厨房,此时服务员没有在厨房等待厨师烹饪菜肴,而是去服务了其他客人,当厨师将你的菜肴烹饪好以后,服务员再将菜肴送到你的面前。也就是说,一个服务员同时可以服务多个客人。
异步API的执行方式:代码在执行过程中某行代码需要耗时,代码的执行不会等待耗时操作完成以后再去执行下一行代码,而是不等待直接向后执行。异步代码的执行结果需要通过回调函数的方式处理。
console.log("before");
setTimeout(() =>
console.log("last");
, 2000);
console.log("after");
2.2 获取异步API的返回值
示例:Node.js中使用回调函数获取异步API的返回值
// getMsg函数定义
function getMsg(callback)
setTimeout(function ()
// 调用callback
callback(
msg: "橘猫吃不胖"
)
, 2000);
// getMsg函数调用
getMsg(function (data)
// 在回调函数中获取异步API执行的结果
console.log(data);
)
运行该代码,在终端2秒后,输出对象:
代码说明:
1、在上面的代码中,首先为getMsg()函数传入了一个参数,该参数同样是一个函数。
2、接收到参数后,getMsg()函数开始执行,这时callback就是function(data)console.log(data);
。
3、在getMsg()内部设置了一个定时器,定时器过2秒就会执行函数function()callback(msg:"橘猫吃不胖")
。
4、2秒后执行定时器内定义的函数,之前分析了callback就是函数function(data)console.log(data);
,因此msg:"橘猫吃不胖"
就是callback的参数,也就是说,该对象就是data
5、最后执行该函数,输出data,结果输出了对象:msg:“橘猫吃不胖”
2.3 异步编程中回调地狱的问题
异步API不能通过返回值的方式获取执行结果,异步API也不会阻塞后续代码的执行。
回调地狱:如果异步API后面代码的执行依赖当前异步API的执行结果,这就需要把代码写在回调函数中。一旦回调函数的嵌套层次过多,就会导致代码不易维护。
回调地狱案例:依次读取A文件、B文件、C文件。通常的做法是:使用fs.readFile()方法读取A文件,A文件读取完成之后,在读取A文件的回调函数中去读取B文件;B文件读取完成之后,在读取B文件的回调函数中去读取C文件。
示例:创建3个文件,分别是A.txt、B.txt、C.txt,文件内容分别是A、B、C,创建callbackhell.js文件,代码如下
const fs = require("fs");
fs.readFile("./A.txt", "utf8", (err, data1) =>
console.log(data1);
fs.readFile("./B.txt", "utf8", (err1, data2) =>
console.log(data2);
fs.readFile("./C.txt", "utf8", (err2, data3) =>
console.log(data3);
)
)
)
运行该程序,终端依次输出A、B、C
2.4 利用Promise解决回调地狱
Promise本身是一个构造函数,如果要使用Promise解决回调地狱的问题,需要使用new关键字创建Promise构造函数的实例对象。
// 定义Promise
let promise = new Promise(((resolve, reject) =>
));
// 定义resove和reject参数函数
promise.then(result => console.log(result))
.catch(error => console.log(error));
示例:使用Promise解决callbackhell.js文件中回调地狱的问题
const fs = require("fs"); // 调用文件系统(fs模块)
function p1()
return new Promise((resolve, reject) =>
fs.readFile("./A.txt", "utf8", (err, data) => resolve(data));
)
function p2()
return new Promise((resolve, reject) =>
fs.readFile("./B.txt", "utf8", (err, data) => resolve(data));
)
function p3()
return new Promise((resolve, reject) =>
fs.readFile("./C.txt", "utf8", (err, data) => resolve(data));
)
p1().then((r1) =>
console.log(r1);
// 使用return返回p2()函数的Promise对象,会在下一个then()中拿到这个Promise对象的结果
return p2();
).then((r2) =>
// 获取上一个Promise对象的结果
console.log(r2);
return p3();
).then((r3) =>
// 获取上一个Promise对象的结果
console.log(r3);
);
2.5 异步函数
异步函数实际上是在Promise对象的基础上进行了封装,它把一些看起来比较繁琐的代码封装起来,然后开放一些关键字供开发者来使用。
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。
async关键字:异步函数需要在function前面加上async关键字。
async function fn()
throw "错误"; // throw代替reject()方法
return 123; // return代替resolve()方法
fn().then(function (data)
// 获取到return的值
console.log(data);
).catch(function (err)
// 捕获throw抛出的错误
console.log(err);
)
await关键字:可以暂停异步函数的执行,等待Promise对象返回结果再向下执行函数。await关键字只能出现在异步函数中,await后面只能写Promise对象,不能写其他类型API。
示例:使用await关键字实现3个异步函数的有序执行
async function p1()
return "p1";
async function p2()
return "p2";
async function p3()
return "p3";
async function run()
let r1 = await p1();
发明炒菜机器,不仅吸引了顾客,他还申请了17项专利