(一)服务器端基础概念
1 .URL
URL的组成:
传输协议://服务器IP或域名:端口/资源所在位置标识
http://astro.sina.com.cn/focus/leo/
http:超文本传输协议,提供了一种发布和接收HTML页面的方法
2 .本机域名
localhost 127.0.0.1
(二)创建web服务器
创建web服务器要用到系统模块http,使用系统模块http下面的createServer方法,返回一个服务器对象,
为服务器对象绑定事件处理,对请求做出响应,最后监听服务器端口。
// 引入http系统模块
const http = require("http");
// 创建服务器对象
let app = http.createServer();
// 添加事件request
app.on("request", (req, res) => {
// 对请求做出响应 参数 req: 请求对象 res: 响应
res.end("<h1>Welcome!</h1>");
})
// 监听端口
app.listen(3000);
// 访问: localhost:3000
(三) HTTP协议
1 .报文:在http请求和响应的过程中传递的数据库就叫报文。包括要传递的数据和一些附加信息,并且要遵守规定好的格式。
请求报文:
①请求方式:
GET 请求数据
POST 响应数据
获取请求方式:通过req请求对象里的method方法,req.method
最常见的get请求就是从地址栏输入地址的方式,相对而言,post请求比get请求要安全,所以post请求方式一般用于提交表单。
const http = require("http");
let app = http.createServer();
app.on("request", (req, res) => {
// 获取请求方式
let method = req.method.toLowerCase();
if (method == "get") {
res.end("get");
} else {
res.end("post");
}
})
app.listen(3000);
获取客户端的请求地址:通过请求对象的url方法,req.url
const http = require("http");
let app = http.createServer();
app.on("request", (req, res) => {
let method = req.method.toLowerCase();
// 获取请求信息
let url = req.url;
if (method == "get") {
// 如果客户端输入地址为/index或者为空
if (url == "/index" || url == "/") {
res.end("Welcome to indexPage");
} else if(url == "/list") {
res.end("Welcome to listPage")
} else {
res.end("NOT FOUND");
}
} else {
// POST
}
})
获取请求报文 :req.headers
获取请求报文属性:如获取请求报文的accpet :req.headers[‘accept’]
②响应报文
HTTP状态码,用于响应给客户端请求的结果
200 请求成功
404 请求资源未找到
500 服务器端错误
400 客户端请求语法错误
HTTP状态码通过res.writeHead()响应给客户端
内容类型:
res.writeHead()第一个参数为HTTP状态码,第二个参数为内容类型
text/plain 纯文本
text/html html文件
text/css css文件
application/javascript JS文件
image/jpeg 图片文件
application/json json文件
res.writeHead(200, {
"content-type": "text/html;charset=utf8"
})
2 .请求参数
客户端向服务端发送请求时,通常要携带一些客户信息,客户信息要通过请求参数的形式传递给服务器,比如登录操作。请求参数分为GET请求参数和POST请求参数。
get请求参数:
http://localhost:3000/list?uname=admin&password=123456
问号后面的信息就为请求参数信息。
获取请求参数:
由于请求参数信息包含在url里面,我们可以通过req.url获取,再通过系统模块url下面的url.parse(req.url, true)方法进行处理,处理后的结果包含了query对象(以键值对形式存储的请求参数信息)和pathname(不包含请求信息的请求地址)。
const http = require("http");
const url = require("url");
let app = http.createServer();
app.on("request", (req, res) => {
res.writeHead(200, {
"content-type": "text/html; charset=utf8"
})
let {query, pathname} = url.parse(req.url, true);
// query: { name: admin, password: 123456 }
let method = req.method.toLowerCase();
if (method == "get") {
if (pathname == "/" || pathname == "/index") {
res.end("欢迎来到首页");
} else if(pathname == "/list") {
res.end("欢迎来到列表页");
} else {
res.end("页面没有找到");
}
} else {
// POST
}
})
post请求参数:
post请求常用来提交表单
<form action="http://localhost:3000" method="post">
账号 <input type="text" placeholder="请输入密码"><br />
密码 <input type="password"><br />
<input type="submit" value="提交">
</form>
post请求参数被放置在请求报文的Form Data中,和get请求的格式是一样的
uname=admin&password=123456
由于post请求参数理论上讲长度是无限的,服务器接收post请求参数不是一次性就接收完的,post请求参数通过data和end事件来分次接收。
在接收post请求之前声明一个变量为空字符串,在触发data事件时,我们将变量进行拼接,在触发end事件时输出拼接完成的变量
这样,就成功获取了客户端的post请求参数:uname=admin&password=123456,下面将获取的结果转换为键值对格式,注意,post请求参数不能使用url模块来处理,node提供了专门用于处理post请求参数的querystring模块中的parse方法。
// const querystring = require("qureystring");
app.on("request", (req, res) => {
let postParams = "";
req.on("data", params => { postParams += params });
req.on("end", () => { console.log(querystring.parse(postParams)) });
res.end("ok");
})
3 .路由
路由是指客户端请求代码与服务器端程序代码的对应关系,简单来说,就说服务器端为响应客户端请求编写的处理逻辑。如以上,我们在request事件里编写的用于判断请求方式和请求地址,响应相应的信息的代码。
4 .静态资源访问
服务器不需要处理,可以直接相应给客户端的资源,就是静态资源,如image,css,JS
// 1.引入http模块
const http = require("http");
const url = require("url");
const path = require("path");
const fs = require("fs");
const mime = require("mime");
// 2 .创建服务器对象
let app = http.createServer();
// 3.请求和相应事件处理
app.on("request", (req, res) => {
// 5.获取客户端请求地址(引入url模块)
let pathname = url.parse(req.url).pathname;
// 11 .处理客户端请求地址为空
pathname = pathname == "/" ? "/index.html" : pathname;
// 6 .拼接请求地址,对应服务器端物理地址(引入path模块)
let realpath = path.join(__dirname, "static", "public" + pathname);
// 7. 读取对应物理路径文件(引入fs模块)
// 10. 指定相应报文的内容类型(引入mime模块,并使用mime.getType方法自动获取对应类型)
let type = mime.getType(realpath);
fs.readFile(realpath, (err, doc) => {
// 8. 如果读取失败,抛出错误
if (err != null) {
res.writeHead(404, {
"content-type": "text/html;charset=utf8"
});
res.end("文件未找到");
}
// 9.如果读取成功,指定相应报文的内容类型并响应相应内容
res.writeHead(200, {
"content-type": type
});
res.end(doc);
})
})
// 4.监听端口
app.listen(3000);
5 .promise
promise构造函数是为了解决回调地狱的问题,它是异步编程语法上的改进,可以让异步API的执行和结果的处理分离,并且可以链式调用。
let promise = new Promise((resolve, reject) => {
// 第一个参数resolve指的是程序执行成功执行的函数
// 第二个参数reject指的是执行失败执行的函数
// 当程序执行完毕后,链式调用then方法来接收执行成功的结果,调用catch方法接收执行失败的错误信息
}).then().catch();
promise是如何解决回调地狱的:
当前需求为依次读取指定目录下的三个文件,用回调函数写法:
const fs = require("fs");
fs.readFile("./1.txt", "utf8", (doc) => {
console.log(doc);
fs.readFile("./2.txt", "utf8", (result) => {
console.log(result);
fs.readFile("./3.txt", "utf8", (tel) => {
console.log(tel);
})
})
})
使用promise改进的写法:
const fs = require("fs");
function first() {
return new Promise((resolve, reject) => {
fs.readFile("./1.txt", "utf8", (err, doc) => {
resolve(doc);
})
})
}
function second() {
return new Promise((resolve, reject) => {
fs.readFile("./2.txt", "utf8", (err, doc) => {
resolve(doc);
})
})
}
function third() {
return new Promise((resolve, reject) => {
fs.readFile("./3.txt", "utf8", (err, doc) => {
resolve(doc);
})
})
}
first().then((doc) => {
console.log(doc)
return second();
}).then((doc) => {
console.log(doc)
return third();
}).then((doc) => {
console.log(doc)
})
异步函数:异步函数是异步编程语法的终极解决方案(ES7),它可以将异步代码写成同步的形式,使结构更清晰的前提下大大简化代码。
在普通函数前面加上 async关键字,普通函数就成了异步函数,它会将函数内部的代码包装成一个promise对象并返回,在异步函数中,我们可以在promise对象前面使用await关键字,它可以暂停异步代码的执行过程,等待异步函数返回结果后再向下执行。
Node.js中的回调函数,根据约定具有统一形式,(err, value)=>{},
因此,我们可以用统一的办法,将接受这种回调函数作为参数的函数,转换为返回promise的函数,node提供了util模块下的promisify方法,用以改造现有node.js中的异步API,方便我们快捷的把原来的异步回调方法改成返回 Promise 实例的方法
使用异步函数改造以上依次读取三个文件的代码如下:
const fs = require("fs");
const util = require("util");
const readFile = util.promisify(fs.readFile);
async function run() {
let first = await readFile("./1.txt", "utf8");
let second = await readFile("./2.txt", "utf8");
let third = await readFile("./3.txt", "utf8");
console.log(first);
console.log(second);
console.log(third);
}
run();