需求:图书的增删改查,图书数据保存在data.json文件中。
1、Demo结构:
2、首先下载安装node.js,配置环境变量:参考博客
3、项目初始化
1)创建项目根目录node-hello,进入到根目录node-hello,右键/在此处打开PowerShell窗口。
2)创建app.js文件。
3)在控制台输入命令:npm init -y,会自动创建package.json文件。
4)在控制台输入命令:npm install mime,会自动创建node_modules文件夹,并下载mime模块到该文件夹,还会自动生成package-lock.json文件。
5)npm install underscore,下载underscore模块。underscore的使用参考博客。
4、入口文件app.js
// 加载http模块 var http = require("http"); var fs = require("fs"); var path = require("path"); var mime = require("mime"); var url = require("url"); var querystring = require("querystring"); var _ = require("underscore"); // 创建一个http服务对象 http.createServer(function(req, res) { req.url = req.url.toLowerCase(); req.method = req.method.toLowerCase(); if (req.method === 'get') { // 通过url模块的parse方法获取用户get请求提交的数据 // 第二个参数为true时,urlObj的query属性就是所有的请求参数,类型是json对象 var urlObj = url.parse(req.url, true); // console.log(urlObj); } // 封装fender函数,将render函数挂在到res对象上;第二个参数是模板数据 res.render = function (filename, templateData) { fs.readFile(filename, function(err, data) { if(err) { res.writeHead(404, 'not found', {'Content-Type': 'text/html; charset=utf-8'}); res.end("404, not found."); return; } if (templateData) { var fn = _.template(data.toString("utf-8")); data = fn(templateData); } res.setHeader('Content-Type', mime.getType(filename)); res.end(data); }); }; if(req.url === '/' || req.url === '/index') { res.render(path.join(__dirname, 'pages/index.html')); } else if(req.url === '/list') { // 第一步:读取data.json文件中的数据,转换为list数组 fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var bookList = JSON.parse(data || '[]'); // json数组 // 第二步:在服务器使用模版引擎,将bookList数据和list.html页面结合 res.render(path.join(__dirname, 'pages/list.html'), {msg:'这是list页面', bookList:bookList}); }); } else if(req.url.startsWith('/add')) { res.render(path.join(__dirname, 'pages/add.html')); } else if(req.url.startsWith('/submit') && req.method === 'get') { // 如何获取get请求的参数 // 用户表单提交get请求数据,保存到data/data.json文件中,新数据以追加的形式添加 console.log(req.url); // /summit?name=xxx&price=xxx // 首先读取data.json文件的内容 fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var list = JSON.parse(data || '[]'); // json数组 // 获取get请求的参数,将用户提交的数据添加到json数组 var book = urlObj.query; // book.id = list.length + 1; book.id = list[list.length - 1].id + 1; list.push(book); console.log("添加一条记录后,data.json的内容:" + JSON.stringify(list)); // 将json数组写入data.json文件 fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function(err) { if(err) throw err; console.log('写入成功'); }); }); // 重定向 res.statusCode = 302; res.statusMessage = 'Found'; res.setHeader('Location', '/index'); res.end(); } else if(req.url.startsWith('/submit') && req.method === 'post') { // 如何获取post请求的参数 var array = []; // 用来保存用户post请求提交的数据 var postBody; req.on('data', function(chunk) { // chunk就是浏览器本次data事件提交的一部分数据;chunk是Buffer类型 console.log("data事件..."); array.push(chunk); }); req.on('end', function() { console.log("end事件..."); // 把array中多个Buffer对象汇总到一个Buffer对象 postBody = Buffer.concat(array); console.log("postBody: " + postBody); // postBody: name=1&price=1 }); fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var list = JSON.parse(data || '[]'); // json数组 // 获取get请求的参数,将用户提交的数据添加到json数组 // querystring对象可以把表单数据 name=xxx&price=xxx => {"name":"xxx","price":"xxx"} var book = querystring.parse(postBody.toString("utf-8")); // book.id = list.length + 1; book.id = list[list.length - 1].id + 1; list.push(book); console.log("添加一条记录后,data.json的内容:" + JSON.stringify(list)); // 将json数组写入data.json文件 fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function(err) { if(err) throw err; console.log('写入成功'); }); }); /* // 首先读取data.json文件的内容 fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var list = JSON.parse(data || '[]'); // json数组 // 获取post请求的参数,将用户提交的数据添加到json数组 // 因为post提交数据的时候,数据量可能比较大,所以可能分多次提交,每次添加一部分数据 // 此时服务器要获取用户提交的所有数据,必须监听request对象的data事件 // 什么时候表示浏览器把所有数据都提交到服务器了呢?当request对象的end事件触发 var array = []; // 用来保存用户post请求提交的数据 console.log("111..."); req.on('data', function(chunk) { // chunk就是浏览器本次data事件提交的一部分数据;chunk是Buffer类型 console.log("data事件..."); array.push(chunk); }); req.on('end', function() { console.log("end事件..."); // 把array中多个Buffer对象汇总到一个Buffer对象 var postBody = Buffer.concat(array); console.log("postBody: " + postBody); list.push(postBody); console.log("添加一条记录后,data.json的内容:" + JSON.stringify(list)); // 将json数组写入data.json文件 fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function(err) { if(err) throw err; console.log('写入成功'); }); }); console.log("222..."); });*/ // 重定向 res.statusCode = 302; res.statusMessage = 'Found'; res.setHeader('Location', '/index'); res.end(); } else if(req.url.startsWith('/detail') && req.method === 'get') { var queryId = urlObj.query.id; // 第一步:读取data.json文件中的数据,转换为list数组 fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var bookList = JSON.parse(data || '[]'); // json数组 // 查询指定id的记录 var book = {}; for (var i = 0; i < bookList.length; i++) { if (bookList[i].id == queryId) { // 不能写“===”,因为queryId是字符串类型 book = bookList[i]; } } // 第二步:在服务器使用模版引擎,将数据和detail.html页面结合 res.render(path.join(__dirname, 'pages/detail.html'), {book:book}); }); } else if(req.url.startsWith('/delete') && req.method === 'get') { var queryId = urlObj.query.id; // 第一步:读取data.json文件中的数据,转换为list数组 fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var bookList = JSON.parse(data || '[]'); // json数组 // 删除指定id的记录 for (var i = 0; i < bookList.length; i++) { if (bookList[i].id == queryId) { bookList.splice(i, 1); // 删除起始下标为queryId-1,长度为1的元素 } } // 将json数组重新写入data.json文件 fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(bookList), function(err) { if(err) throw err; console.log('写入成功'); }); // 第二步:在服务器使用模版引擎,将bookList数据和list.html页面结合 res.render(path.join(__dirname, 'pages/list.html'), {msg:'这是list页面', bookList:bookList}); }); } else if(req.url.includes('static')){ res.render(path.join(__dirname, req.url)); } else if(req.url.startsWith('/edit') && req.method === 'post') { var array = []; // 用来保存用户post请求提交的数据 var postBody; req.on('data', function(chunk) { // chunk就是浏览器本次data事件提交的一部分数据;chunk是Buffer类型 console.log("data事件..."); array.push(chunk); }); req.on('end', function() { console.log("end事件..."); // 把array中多个Buffer对象汇总到一个Buffer对象 postBody = Buffer.concat(array); }); fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} var bookList = JSON.parse(data || '[]'); // json数组 // 获取get请求的参数,将用户提交的数据添加到json数组 // querystring对象可以把表单数据 name=xxx&price=xxx => {"name":"xxx","price":"xxx"} var book = querystring.parse(postBody.toString("utf-8")); console.log("=============book:" + book.id + book.name + book.price); for (var i = 0; i < bookList.length; i++) { // console.log("bookList[i].id=" + bookList[i].id); // console.log("book.id=" + book.id); // console.log(bookList[i].id == book.id); if(bookList[i].id == book.id) { bookList[i].name = book.name; bookList[i].price = book.price; } } // 将json数组重新写入data.json文件 fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(bookList), function(err) { if(err) throw err; console.log('写入成功'); }); // 重定向 res.statusCode = 302; res.statusMessage = 'Found'; res.setHeader('Location', '/list'); res.end(); }); } else { res.writeHead(404, 'not found', {'Content-Type': 'text/html; charset=utf-8'}); res.end("404,not found!"); } }).listen('8080', function() { console.log('服务器已经启动,请访问http://127.0.0.1:8080/'); });
5、common.css
h2 { background-color: #ccc; }
6、html页面
add.html
<!DOCTYPE html> <html> <head> <title>add页面</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="/static/css/common.css"> </head> <body> <h2>add页面</h2> <form method="get" action="/submit"> <!-- <form method="post" action="/submit"> --> 书名: <input type="text" name="name"> 价格: <input type="text" name="price"> <input type="submit" value="添加"> </form> </body> </html>
detail.html
<!DOCTYPE html> <html> <head> <title>detail页面</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="/static/css/common.css"> </head> <body> <h2>detail页面</h2> <form method="post" action="/edit"> <input type="hidden" name="id" value="<%= book.id %>"> 书名:<input type="text" name="name" value="<%= book.name %>"><br/> 价格:<input type="text" name="price" value="<%= book.price %>"><br/> <input type="submit" value="修改"> </form> </body> </html>
index.html
<!DOCTYPE html> <html> <head> <title>index页面</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="/static/css/common.css"> </head> <body> <h2>index页面</h2> <!-- <img src="/static/images/1.jpg"> <img src="/static/images/2.png"> --> <a href="/list">图书列表</a> </body> </html>
list.html
<!DOCTYPE html> <!DOCTYPE html> <html> <head> <title>list页面</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="/static/css/common.css"> </head> <body> <h2>list页面</h2> <%= msg %>!</h2><br/> <!-- <img src="/static/images/1.jpg"> <img src="/static/images/2.png"> --> <a href="/add">添加</a> <table border="1" cellpadding="0"> <tr> <td>ID</td> <td>书名</td> <td>价格</td> <td>操作</td> </tr> <% for(var i = 0; i < bookList.length; i++) { %> <tr> <td><%= bookList[i].id %></td> <td><%= bookList[i].name %></td> <td><%= bookList[i].price %></td> <td> <a href="/detail?id=<%= bookList[i].id %>">查看</a> <a href="/delete?id=<%= bookList[i].id %>">删除</a> </td> </tr> <% } %> </table> </body> </html>
7、node app.js启动项目,浏览器访问主页 :http://127.0.0.1:8080/
测试完后data.json文件的数据:
[{"id":1,"name":"射雕英雄传","price":"45.00"},{"id":2,"name":"天龙八部2","price":"66.00"},
{"id":3,"name":"倚天屠龙记","price":"50.00"},{"id":4,"name":"Java编程思想2","price":"40.00"},
{"name":"Java从入门到精通","price":"30.00","id":6},{"name":"ggf","price":"fff","id":8},
{"name":"af","price":"ff","id":9}]
8、封装app.js里面重复代码
// 加载http模块 var http = require("http"); var fs = require("fs"); var path = require("path"); var mime = require("mime"); var url = require("url"); var querystring = require("querystring"); var _ = require("underscore"); // 创建一个http服务对象 http.createServer(function(req, res) { req.url = req.url.toLowerCase(); req.method = req.method.toLowerCase(); if (req.method === 'get') { // 通过url模块的parse方法获取用户get请求提交的数据 // 第二个参数为true时,urlObj的query属性就是所有的请求参数,类型是json对象 var urlObj = url.parse(req.url, true); // console.log(urlObj); } // 封装fender函数,将render函数挂在到res对象上;第二个参数是模板数据 res.render = function (filename, templateData) { fs.readFile(filename, function(err, data) { if(err) { res.writeHead(404, 'not found', {'Content-Type': 'text/html; charset=utf-8'}); res.end("404, not found."); return; } if (templateData) { var fn = _.template(data.toString("utf-8")); data = fn(templateData); } res.setHeader('Content-Type', mime.getType(filename)); res.end(data); }); }; if(req.url === '/' || req.url === '/index') { res.render(path.join(__dirname, 'pages/index.html')); } else if(req.url === '/list') { // 第一步:读取data.json文件中的数据,转换为list数组 readFile(function(bookList) { // 第二步:使用模版引擎,将bookList数据和list.html页面结合 res.render(path.join(__dirname, 'pages/list.html'), {msg:'这是list页面', bookList:bookList}); }); } else if(req.url.startsWith('/add')) { res.render(path.join(__dirname, 'pages/add.html')); } else if(req.url.startsWith('/submit') && req.method === 'get') { // 如何获取get请求的参数 // 用户表单提交get请求数据,保存到data/data.json文件中,新数据以追加的形式添加 console.log(req.url); // /summit?name=xxx&price=xxx // 第一步:读取data.json文件中的数据,转换为list数组 readFile(function(list) { // 第二步:获取get请求的参数,将用户提交的数据添加到json数组 var book = urlObj.query; book.id = list[list.length - 1].id + 1; list.push(book); console.log("添加一条记录后,data.json的内容:" + JSON.stringify(list)); // 第三步:将json数组写入data.json文件,并重定向到list页面 writeFile(list, res); }); } else if(req.url.startsWith('/submit') && req.method === 'post') { // 如何获取post请求的参数 // 用户表单提交post请求数据,保存到data/data.json文件中,新数据以追加的形式添加 getPostBodyData(req, function(book) { // post请求数据封装成book(json对象) // 读取data.json文件中的数据,转换为list数组 readFile(function(list) { book.id = list[list.length - 1].id + 1; list.push(book); // console.log("添加一条记录后,data.json的内容:" + JSON.stringify(list)); // 将json数组写入data.json文件,并重定向到list页面 writeFile(list, res); }); }); } else if(req.url.startsWith('/detail') && req.method === 'get') { // 第一步:读取data.json文件中的数据,转换为list数组 readFile(function(bookList) { // 第二步:查询指定id的记录 var book = {}; for (var i = 0; i < bookList.length; i++) { // 不能写"===",因为urlObj.query.id是字符串类型 if (bookList[i].id == urlObj.query.id) { book = bookList[i]; break; } } // 第三步:使用模版引擎,将数据和detail.html页面结合 res.render(path.join(__dirname, 'pages/detail.html'), {book:book}); }); } else if(req.url.startsWith('/delete') && req.method === 'get') { // 第一步:读取data.json文件中的数据,转换为list数组 readFile(function(bookList) { // 第二步:删除指定id的记录 for (var i = 0; i < bookList.length; i++) { if (bookList[i].id == urlObj.query.id) { bookList.splice(i, 1); // 删除起始下标为queryId-1,长度为1的元素 break; } } // 第三步:将json数组重新写入data.json文件,并重定向到list页面 writeFile(bookList, res); }); } else if(req.url.includes('static')){ res.render(path.join(__dirname, req.url)); } else if(req.url.startsWith('/edit') && req.method === 'post') { getPostBodyData(req, function(book) { // post请求数据封装成book(json对象) console.log("=============book:" + book.id + book.name + book.price); // 读取data.json文件中的数据,转换为list数组 readFile(function(bookList) { for (var i = 0; i < bookList.length; i++) { // console.log("bookList[i].id=" + bookList[i].id); // console.log("book.id=" + book.id); console.log(bookList[i].id == book.id); if(bookList[i].id == book.id) { bookList[i].name = book.name; bookList[i].price = book.price; break; } } // 将json数组重新写入data.json文件,并重定向到list页面 writeFile(bookList, res); }); }); } else { res.writeHead(404, 'not found', {'Content-Type': 'text/html; charset=utf-8'}); res.end("404,not found!"); } }).listen('8080', function() { console.log('服务器已经启动,请访问http://127.0.0.1:8080/'); }); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 封装读取data.json文件的方法 function readFile(callback) { fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf-8', function (err, data) { if(err && err.code !== 'ENOENT') {throw err;} console.log("data.json原来的内容:" + data); // [{"name":"xxx","price":"xxx"}]] var bookList = JSON.parse(data || '[]'); // json数组 callback(bookList); }); } // 封装写入data.json文件的方法 function writeFile(list, res) { // 参数list的类型:json数组 fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function(err) { if(err) throw err; console.log('写入成功'); // 重定向 res.statusCode = 302; res.statusMessage = 'Found'; res.setHeader('Location', '/list'); res.end(); // 使用模版引擎,将bookList数据和list.html页面结合 // res.render(path.join(__dirname, 'pages/list.html'), {msg:'这是list页面', bookList:bookList}); }); } // 封装写入data.json文件的方法 function writeFile2(data, callback) { // 参数data的类型:字符串 fs.writeFile(path.join(__dirname, 'data', 'data.json'), data, function(err) { if(err) throw err; console.log('写入成功'); // 调用callback()来执行当写入数据完毕后的操作 callback(); }); } // 封装获取用户post提交的数据的方法 function getPostBodyData(req, callback) { var array = []; // 用来保存用户post请求提交的数据 var postBody; req.on('data', function(chunk) { // chunk就是浏览器本次data事件提交的一部分数据;chunk是Buffer类型 console.log("data事件..."); array.push(chunk); }); req.on('end', function() { console.log("end事件..."); // 把array中多个Buffer对象汇总到一个Buffer对象 var postBody = Buffer.concat(array); // 此处postBody类型:Buffer // console.log("postBody: " + postBody); // postBody: name=1&price=1 // querystring对象可以把表单数据 name=xxx&price=xxx => {"name":"xxx","price":"xxx"} postBody = querystring.parse(postBody.toString("utf-8")); // 此处postBody类型:json对象 callback(postBody); }); }