一. 区别
没有请求数据库,就是静态服务器。
请求了数据库,就是动态服务器。
二. 动态服务器
用json文件当作数据库。
//users.json
[
{"id":"1","name":"anqi","password":"xxx"},
{"id":"2","name":"susu","password":"zzz"}
]
1. 读数据
//test.js
const fs = require("fs");
const usersString = fs.readFileSync("./db/users.json").toString();
const usersArray = JSON.parse(usersString); //反序列化
2. 存数据
const user3 = { id: 3, name: "jane", password: "qqq" };
usersArray.push(user3);
const string = JSON.stringify(usersArray); //序列化
fs.writeFileSync("./db/users.json", string);
运行 node test.js
3. 实现用户注册功能
用户提交用户名和密码,users.json就新增了一行数据。
思路:
前端:
- 在注册页面,写一个form表单,用户填写用户名和密码
- 监听form的submit事件,当用户点击注册按钮时,触发事件
- 向服务器发送POST请求,数据(得到的用户填写的用户名和密码)位于请求体
后端:
- 接收post请求
- 获取请求体中的数据
- 得到数据完成后,把数据存进数据库(users.json)
前端代码:
//register.html
<form id="registerForm">
<div> //经常用label标签包住input
<label>用户名 <input type="text" name="name" /></label>
</div>
<div>
<label>密码 <input type="password" name="password" /></label>
</div>
<div>
<button type="submit">注册</button>
</div>
</form>
//用jquery库
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
const $form = $("#registerForm");
$form.on("submit", (e) => {
e.preventDefault(); //阻止表单的默认事件,使点击按钮不刷新
//找到form里的name=name的input标签,得到填入的值
const name = $form.find("input[name=name]").val();
const password = $form.find("input[name=password]").val();
//用jquery.ajax发送一个POST请求
$.ajax({
method: "POST",
url: "/register",
contentType: "text/json;charset=UTF-8",
data: JSON.stringify({ name, password }), //把要上传的数据放到data里
}).then(() => {
alert("注册成功");
location.href = "/sign_in.html";
});
});
</script>
在后端服务器代码里,新开一个路由
//server.js
if (path === "/register" && method === "POST") {
response.setHeader("Content-Type", "text/html;charset=utf-8");
//先把数据库读出来
const usersArray = JSON.parse(fs.readFileSync("./db/users.json"));
const lastUser = usersArray[usersArray.length - 1];
const array = [];
request.on("data", (chunk) => {
array.push(chunk);
});
request.on("end", () => {
//把utf-8编码的array变成字符串形式
const string = Buffer.concat(array).toString();
const obj = JSON.parse(string);
const newUser = {
id: lastUser ? lastUser.id + 1 : 1,
name: obj.name,
password: obj.password,
};
usersArray.push(newUser);
fs.writeFileSync("./db/users.json", JSON.stringify(usersArray));
response.end();
});
}
获取到GET请求携带的内容,可以用query,查询参数。POST请求可以监听request的"data"事件,由于POST请求上传的数据可能很大,需要一点点接收,所以,监听到数据传过来一点,就接收一点。什么时候数据接收完毕呢?监听request的"end"事件,数据接收结束了,就转换成正确的格式,得到传过来的数据,把它放进数据库。
4. 实现用户登录功能
1. 思路
- 登录页sign_in.html,用户填写用户名和密码,并且提交
- 首页home.html,用户点击登录,跳转到首页,已登录的用户可以看到自己的用户名
- 输入的用户名和密码如果匹配,即数据库里能找到,就跳转。
前端:
- 在登录页面写一个form表单,供用户提交用户名和密码
- 监听form的submit事件,当监听到用户点击按钮时,触发相关事件
- 发送POST请求,数据位于请求体
后端:
- 接收POST请求
- 获取到请求体中的数据,name和password
- 去数据库中查找,是否和上传的name,password相匹配
- 如果匹配,标记用户已登录,在home.html里显示已登录
前端代码:
//sign_in.html
<form id="signInForm">
<div>
<label>用户名 <input type="text" name="name" /></label>
</div>
<div>
<label>密码 <input type="password" name="password" /></label>
</div>
<div>
<button type="submit">登录</button>
</div>
</form>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
const $form = $("#signInForm");
$form.on("submit", (e) => {
e.preventDefault();
const name = $form.find("input[name=name]").val();
const password = $form.find("input[name=password]").val();
$.ajax({
method: "POST",
url: "/sign_in", //请求/sign_in路由
contentType: "text/json;charset=UTF-8",
data: JSON.stringify({ name, password }),
}).then(() => {
alert("登录成功");
location.href = "/home.html"; //点击确定按钮后,跳转到home.html
});
});
</script>
后端服务器代码:
//server.js
if (path === "/sign_in" && method === "POST") { //必须是POST请求
response.setHeader("Content-Type", "text/html;charset=utf-8");
const usersArray = JSON.parse(fs.readFileSync("./db/users.json"));
const array = [];
request.on("data", (chunk) => {
array.push(chunk);
});
request.on("end", () => {
const string = Buffer.concat(array).toString();
const obj = JSON.parse(string); //obj.name,obj.password得到了
const user = usersArray.find(function (user) {
return user.name === obj.name && user.password === obj.password;
});
//如果有匹配的,请求成功
if (user) {
response.statusCode = 200;
//设置cookie
response.setHeader("Set-Cookie", "logined=1");
response.end();
} else {
response.statusCode = 400; //错了
response.setHeader("Content-Type", "text/json;charset=utf-8");
response.end(`{"errorCode":531}`); //错误码是自己定的,响应json字符串
}
});
}
在首页home.html,先设置一个占位符。怎么区分是从登陆页面跳转的(应该显示已登录),还是直接在地址栏输入的home.html呢(应该显示未登录)?
2. 使用Cookie标记用户

用户在sign_in.html页面点击登录时,就会向 /sign_in路由发送POST请求,如果成功,通过then方法先弹出登录成功的提示框,点击确定后,跳转到首页。
服务器接收到向/sign_in的请求,先拿到POST上传的数据,去数据库里查找有没有这个用户,如果有,就代表请求成功,并且给浏览器发一个cookie,标记此用户已登录。如果没有这个用户,就失败,返回一段错误码。
同时,如果请求成功了,在客户端就依次弹出提示框,再去请求首页。服务端接收到浏览器发来的/home.html的请求,先看看浏览器带不带cookie,如果带了,服务器就知道已登录,就把首页里的占位符替换成‘已登录’字符串,响应回去;如果服务器发现浏览器发过来的请求没有cookie,就说明没登录,就显示‘未登录’字符串,作为响应。
response.setHeader("Set-Cookie", "logined=1")
//server.js
else if (path === "/home.html") {
const cookie = request.headers["cookie"];
if (cookie === "logined=1") {
const homeHtml = fs.readFileSync("./public/home.html").toString();
const string = homeHtml.replace("{{loginStatus}}", "已登录");
response.write(string);
} else {
const homeHtml = fs.readFileSync("./public/home.html").toString();
const string = homeHtml.replace("{{loginStatus}}", "未登录");
response.write(string);
}
response.end();
}
也就是说,服务器在浏览器请求/sign_in时,经查找如果有这个用户,就给浏览器发一个cookie。也是服务器在浏览器再次对home.html发起请求时,读取请求头里的cookie。都是在后端操作的。

response.setHeader("Set-Cookie", "logined=1; HttpOnly")
3. 使用cookie记录user.id
现在我们使用cookie标记了用户是否登录,如果已登录,我们还想显示登录的用户名。只需要在/sign_in路由里,如果这个用户存在,把cookie的值由'logined=1'改成标记这个用户的id。
//server.js
if (user) {
response.statusCode = 200;
response.setHeader("Set-Cookie", `user_id=${user.id}; HttpOnly`);
response.end();
}
然后浏览器就带着cookie回去了。紧接着浏览器发起对首页的请求,在服务器代码里,如果请求了首页,先得到随着请求来的cookie,从中拿到user.id,去数据库里找对应的用户,得到user.name,还是用字符串的replace方法,把home.html里的内容换成用户名响应回去。
else if (path === "/home.html") {
const homeHtml = fs.readFileSync("./public/home.html").toString();
const cookie = request.headers["cookie"];
let userId;
try {
userId = cookie
.split(";")
.filter((s) => s.indexOf("user_id=") >= 0)[0]
.split("=")[1];
} catch (error) {}
if (userId) {
const usersArray = JSON.parse(fs.readFileSync("./db/users.json"));
const user = usersArray.find((user) => user.id.toString() === userId)
let string;
string = homeHtml
.replace("{{loginStatus}}", "已登录")
.replace("{{username}}", user.name);
response.write(string);
response.end();
} else {
let string = homeHtml
.replace("{{loginStatus}}", "未登录")
.replace("{{username}}", "");
response.write(string);
response.end();
}
}
在home.html里做的事情就是,先设置两个占位符,根据是否有cookie,以及从cookie中得到的user.id,把占位符替换成真正要显示的内容。
登录成功,点击确定后,跳转到首页:

在无痕窗口直接输入首页的地址,就不会有cookie:

4. session会话保存用户信息
上边的代码有一个bug,我们把user.id写到cookie里,就导致用户可以篡改。怎么防止用户篡改呢?不把user.id这个信息展示出来,而是把它放到服务器里的session.json文件里,再给信息一个随机的id,我们把这个随机数放到cookie里给浏览器。这样,后端下次接收到home.html的请求时,读出来这个随机的id,然后去session.json文件里,通过session[id]读到真正的用户id。
//server.js /sign_in
if (user) {
response.statusCode = 200;
const random = Math.random();
const session = JSON.parse(fs.readFileSync("./session.json"));
session[random] = { user_id: user.id };
fs.writeFileSync("./session.json", JSON.stringify(session));
response.setHeader("Set-Cookie", `session_id=${random}; HttpOnly`);
response.end();
}
在服务器给浏览器发cookie的同时,就开启了session会话,把session数据存进session.json文件里,这个文件就保存了用户的信息。当用户点击登录并且登陆成功时,浏览器的响应里就会出现cookie:

