1、从设计路由开始

1.1 版本一
// 当前项目(包) 的入口文件// 1. 加载 http 模块
var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = require('mime');// 2. 创建服务
http.createServer(function (req, res) {// 设计路由:路由由两部分组成:请求方法+请求路径// 当用户请求 / 或 /index 时,显示新闻列表 - get 请求// 当用户请求 /item 时,显示新闻详情 - get 请求// 当用户请求 /submit 时,显示添加新闻页面 - get 请求// 当用户请求 /add 时,将用户提交的新闻保存到 data.json 文件中 - get 请求// 当用户请求 /add 时,将用户提交的新闻保存到 data.json 文件中 - post 请求// 将用户请求的url 和 method 转换为小写字母req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();// 先根据用户请求的路径(路由),将对应的HTML页面显示出来if (req.url === '/' || req.url === '/index' && req.method === 'get') {// 读取 index.html 并返回fs.readFile(path.join(__dirname, 'views', 'index.html'), function (err, data) {if (err) {throw err;}res.end(data);});} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回fs.readFile(path.join(__dirname, 'views', 'submit.html'), function (err, data) {if (err) {throw err;}res.end(data);});} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回fs.readFile(path.join(__dirname, 'views', 'details.html'), function (err, data) {if (err) {throw err;}res.end(data);});} else if (req.url === '/add' && req.method === 'get') {// 表示 get 方法提交一条新闻} else if (req.url === '/add' && req.method === 'post') {// 表示 post 方法提交一条新闻} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.giffs.readFile(path.join(__dirname, req.url), function (err, data) {if (err) {res.writeHead(404, 'Not Found', { 'Content-Type': 'text/html;charset=utf-8' });res.end('<h2>404, not found.</h2>');return;  // 必要}res.setHeader('Content-Type', mime.getType(req.url));res.end(data);});} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('<h2>404, Page Not Found.</h2>');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});
1.2 版本二:封装一个 render() 函数
var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = require('mime');http.createServer(function (req, res) {req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();if (req.url === '/' || req.url === '/index' && req.method === 'get') {// 读取 index.htmlrender(path.join(__dirname, 'views', 'index.html'), res);} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回render(path.join(__dirname, 'views', 'submit.html'), res);} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回render(path.join(__dirname, 'views', 'details.html'), res);} else if (req.url === '/add' && req.method === 'get') {// 表示 get 方法提交一条新闻} else if (req.url === '/add' && req.method === 'post') {// 表示 post 方法提交一条新闻} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.gifrender(path.join(__dirname, req.url), res);} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});// 封装一个 render 函数
function render(filename, res) {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;}res.setHeader('Content-Type', mime.getType(filename));res.end(data);});
}
1.3 版本三:将 render 函数挂在到 res 对象上
// 当前项目(包) 的入口文件// 封装一个 render() 函数
// 将 render 函数挂在到 res 对象上,可以通过 res.render() 来访问// 1. 加载 http 模块
var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = require('mime');// 2. 创建服务
http.createServer(function (req, res) {// 要在这里写大量的代码// 为 res 对象添加一个 render() 函数,方便后续使用res.render = function (filename) {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;}res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();// 先根据用户请求的路径(路由),将对应的HTML页面显示出来if (req.url === '/' || req.url === '/index' && req.method === 'get') {// 读取 index.htmlres.render(path.join(__dirname, 'views', 'index.html'));} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回res.render(path.join(__dirname, 'views', 'submit.html'));} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回res.render(path.join(__dirname, 'views', 'details.html'));} else if (req.url === '/add' && req.method === 'get') {// 表示 get 方法提交一条新闻} else if (req.url === '/add' && req.method === 'post') {// 表示 post 方法提交一条新闻} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.gifres.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});

2、实现 get 方式添加新闻并跳转到主页面

加载url模块:var url = require('url');

URL 对象:http://nodejs.cn/api/url.html#url_url_strings_and_url_objects

┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                              href                                              │
├──────────┬──┬─────────────────────┬────────────────────────┬───────────────────────────┬───────┤
│ protocol │  │        auth         │          host          │           path            │ hash  │
│          │  │                     ├─────────────────┬──────┼──────────┬────────────────┤       │
│          │  │                     │    hostname     │ port │ pathname │     search     │       │
│          │  │                     │                 │      │          ├─┬──────────────┤       │
│          │  │                     │                 │      │          │ │    query     │       │
"  https:   //    user   :   pass   @ sub.example.com : 8080   /p/a/t/h  ?  query=string   #hash "
│          │  │          │          │    hostname     │ port │          │                │       │
│          │  │          │          ├─────────────────┴──────┤          │                │       │
│ protocol │  │ username │ password │          host          │          │                │       │
├──────────┴──┼──────────┴──────────┼────────────────────────┤          │                │       │
│   origin    │                     │         origin         │ pathname │     search     │ hash  │
├─────────────┴─────────────────────┴────────────────────────┴──────────┴────────────────┴───────┤
│                                              href                                              │
└────────────────────────────────────────────────────────────────────────────────────────────────┘

通过 url 模块,调用 url.parse() 方法解析用户请求的 url(req.url)

var urlObj = url.parse(req.url);
console.log(urlObj);

运行:http://127.0.0.1:8080/add?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83

Url {protocol: null,slashes: null,auth: null,host: null,port: null,hostname: null,hash: null,search: '?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83',  query: 'title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83',  pathname: '/add',path: '/add?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83',href: '/add?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83'
}
var urlObj = url.parse(req.url, true);
console.log(urlObj);

运行:http://127.0.0.1:8080/add?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83

Url {protocol: null,slashes: null,auth: null,host: null,port: null,hostname: null,hash: null,search: '?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83',  query: [Object: null prototype] {title: '未脱单的小码农',url: 'www.coding.onefine.top',text: 'hahah天天开心'},pathname: '/add',path: '/add?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83',href: '/add?title=%E6%9C%AA%E8%84%B1%E5%8D%95%E7%9A%84%E5%B0%8F%E7%A0%81%E5%86%9C&url=www.coding.onefine.top&text=hahah%E5%A4%A9%E5%A4%A9%E5%BC%80%E5%BF%83'
}

服务器通过设置http响应报文头实现客户端浏览器重定向操作:

res.statusCode = 302;  // 3开头表示跳转;301表示页面永久移动到某个位置,302表示临时移动
res.statusMessage = 'Found';
res.setHeader('Location', '/');  // 服务器告诉浏览器跳转位置
res.end();  // 结束响应
2.1 版本一

完整代码如下:

// 当前项目(包) 的入口文件// 封装一个 render() 函数
// 将 render 函数挂在到 res 对象上,可以通过 res.render() 来访问// 1. 加载 http 模块
var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = require('mime');
var url = require('url');// 2. 创建服务
http.createServer(function (req, res) {// 通过 url 模块,调用 url.parse() 方法解析用户请求的 url(req.url)var urlObj = url.parse(req.url, true);// console.log(urlObj);// 为 res 对象添加一个 render() 函数,方便后续使用res.render = function (filename) {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;}res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();// 先根据用户请求的路径(路由),将对应的HTML页面显示出来if (req.url === '/' || req.url === '/index' && req.method === 'get') {// 读取 index.htmlres.render(path.join(__dirname, 'views', 'index.html'));} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回res.render(path.join(__dirname, 'views', 'submit.html'));} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回res.render(path.join(__dirname, 'views', 'details.html'));} else if (req.url.startsWith('/add') && req.method === 'get') {// /add?title=aaaaaaaaaaa&url=http%3A%2F%2Flocalhost%3A9090%2Fsubmit&text=bbbbbbbbbbbbb// 表示 get 方法提交一条新闻// 要获取用户 get 提交的数据,需要用到 url 模块(这个模块是node.js 内置模块,不是第三方模块)// 既然是 get 提交数据,所以通过 req.url 就可以直接获取这些数据,但是这样使用起来不方便(得自己去截取字符串,然后获取想要的数据)// 通过 url 模块,可以将用户 get 提交的数据解析成一个 json 对象,使用起来很方便// console.log(req.url);// res.end('over');// 1. 获取用户 get 提交过来的新闻数据// urlObj.query.title// urlObj.query.url// urlObj.query.text// 2. 把用户提交的新闻数据保存到 data.json 文件中var list = [];list.push(urlObj.query);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {// JSON.stringify(list) 从一个对象解析出字符串if(err)throw err;console.log("ok");// 设置响应报文头,通过响应报文头告诉浏览器,执行一次页面跳转操作// 3. 跳转到新闻列表页——重定向res.statusCode = 302;  // 3开头表示跳转;301表示页面永久移动到某个位置,302表示临时移动res.statusMessage = 'Found';res.setHeader('Location', '/');  // 服务器告诉浏览器跳转位置res.end();  // 结束响应})// res.end(req.url);} else if (req.url.startsWith('/add') && req.method === 'post') {// 表示 post 方法提交一条新闻} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.gifres.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});
2.2 版本二:进一步完善

进一步完善,防止data.json文件被覆盖掉:

// 当前项目(包) 的入口文件// 封装一个 render() 函数
// 将 render 函数挂在到 res 对象上,可以通过 res.render() 来访问// 1. 加载 http 模块
var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = require('mime');
var url = require('url');// 2. 创建服务
http.createServer(function (req, res) {// 通过 url 模块,调用 url.parse() 方法解析用户请求的 url(req.url)var urlObj = url.parse(req.url, true);// console.log(urlObj);// 为 res 对象添加一个 render() 函数,方便后续使用res.render = function (filename) {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;}res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();// 先根据用户请求的路径(路由),将对应的HTML页面显示出来if (req.url === '/' || req.url === '/index' && req.method === 'get') {// 读取 index.htmlres.render(path.join(__dirname, 'views', 'index.html'));} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回res.render(path.join(__dirname, 'views', 'submit.html'));} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回res.render(path.join(__dirname, 'views', 'details.html'));} else if (req.url.startsWith('/add') && req.method === 'get') {// /add?title=aaaaaaaaaaa&url=http%3A%2F%2Flocalhost%3A9090%2Fsubmit&text=bbbbbbbbbbbbb// 表示 get 方法提交一条新闻// 要获取用户 get 提交的数据,需要用到 url 模块(这个模块是node.js 内置模块,不是第三方模块)// 既然是 get 提交数据,所以通过 req.url 就可以直接获取这些数据,但是这样使用起来不方便(得自己去截取字符串,然后获取想要的数据)// 通过 url 模块,可以将用户 get 提交的数据解析成一个 json 对象,使用起来很方便// console.log(req.url);// res.end('over');// 1. 获取用户 get 提交过来的新闻数据// urlObj.query.title// urlObj.query.url// urlObj.query.text// 1.1 读取 data.json 文件中的数据,并将读取到的数据转换为一个数组// 此处,读取文件的时候可以直接写一个 utf8 编码,这样的话,回调函数中的 data 就是一个字符串了fs.readFile(path.join(__dirname, 'data', 'data.json',), 'utf8', function(err, data) {// 因为第一次访问网站, data.json 文件本身就不存在,所以肯定是有错误的// 但是这种错误,我们并不认为是网站出错了,所以不需要抛出异常if (err & err.code !== 'Enoent')  // 不是文件不存在错误throw err;// 如果读取到数据(data.json文件存在),那么就把读取到的数据 data,转换为 list数组// 如果没有读取到数据,那么就把 '[]' 转换为数组var list = JSON.parse(data || '[]');  // 将string转换为json对象// 向数组对象 list 中 push 一条新添加的新闻list.push(urlObj.query);// 2. 把用户提交的新闻数据保存到 data.json 文件中fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {// JSON.stringify(list) 从一个对象解析出字符串if(err)throw err;console.log("ok");// 设置响应报文头,通过响应报文头告诉浏览器,执行一次页面跳转操作// 3. 跳转到新闻列表页——重定向res.statusCode = 302;  // 3开头表示跳转;301表示页面永久移动到某个位置,302表示临时移动res.statusMessage = 'Found';res.setHeader('Location', '/');  // 服务器告诉浏览器跳转位置res.end();  // 结束响应})})// res.end(req.url);} else if (req.url.startsWith('/add') && req.method === 'post') {// 表示 post 方法提交一条新闻} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.gifres.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});

3、实现post方式提交一条新闻

加载querystring模块:var querystring = require('querystring');
querystring对象:http://nodejs.cn/api/querystring.html

// 当前项目(包) 的入口文件var http = require('http');
var fs = require('fs');
var path = require('path');
var mime = require('mime');
var url = require('url');
var querystring = require('querystring');// 2. 创建服务
http.createServer(function (req, res) {// 通过 url 模块,调用 url.parse() 方法解析用户请求的 url(req.url)var urlObj = url.parse(req.url, true);// console.log(urlObj);// 为 res 对象添加一个 render() 函数,方便后续使用res.render = function (filename) {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;}res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();// 先根据用户请求的路径(路由),将对应的HTML页面显示出来if (req.url === '/' || req.url === '/index' && req.method === 'get') {// 读取 index.htmlres.render(path.join(__dirname, 'views', 'index.html'));} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回res.render(path.join(__dirname, 'views', 'submit.html'));} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回res.render(path.join(__dirname, 'views', 'details.html'));} else if (req.url.startsWith('/add') && req.method === 'get') {// /add?title=aaaaaaaaaaa&url=http%3A%2F%2Flocalhost%3A9090%2Fsubmit&text=bbbbbbbbbbbbb// 表示 get 方法提交一条新闻// 要获取用户 get 提交的数据,需要用到 url 模块(这个模块是node.js 内置模块,不是第三方模块)// 既然是 get 提交数据,所以通过 req.url 就可以直接获取这些数据,但是这样使用起来不方便(得自己去截取字符串,然后获取想要的数据)// 通过 url 模块,可以将用户 get 提交的数据解析成一个 json 对象,使用起来很方便// console.log(req.url);// res.end('over');// 1. 获取用户 get 提交过来的新闻数据// urlObj.query.title// urlObj.query.url// urlObj.query.text// 1.1 读取 data.json 文件中的数据,并将读取到的数据转换为一个数组// 此处,读取文件的时候可以直接写一个 utf8 编码,这样的话,回调函数中的 data 就是一个字符串了fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {// 因为第一次访问网站, data.json 文件本身就不存在,所以肯定是有错误的// 但是这种错误,我们并不认为是网站出错了,所以不需要抛出异常if (err & err.code !== 'Enoent')  // 不是文件不存在错误throw err;// 如果读取到数据(data.json文件存在),那么就把读取到的数据 data,转换为 list数组// 如果没有读取到数据,那么就把 '[]' 转换为数组var list = JSON.parse(data || '[]');  // 将string转换为json对象// 向数组对象 list 中 push 一条新添加的新闻list.push(urlObj.query);// 2. 把用户提交的新闻数据保存到 data.json 文件中fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {// JSON.stringify(list) 从一个对象解析出字符串if (err)throw err;// console.log("ok");// 设置响应报文头,通过响应报文头告诉浏览器,执行一次页面跳转操作// 3. 跳转到新闻列表页——重定向res.statusCode = 302;  // 3开头表示跳转;301表示页面永久移动到某个位置,302表示临时移动res.statusMessage = 'Found';res.setHeader('Location', '/');  // 服务器告诉浏览器跳转位置res.end();  // 结束响应})})// res.end(req.url);} else if (req.url === '/add' && req.method === 'post') {// 注意这里为post提交方式,使用req.url为'/add'即可// 表示 post 方法提交一条新闻// 1. 读取 data.json 文件中的数据fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'ENOENT')throw err;var list = JSON.parse(data || '[]');// 2. 获取用户 post 提交的数据// 因为 post 提交数据的时候,数据量可能比较大,所以会分多次进行提交,每次提交一部分数据// 此时要想在服务器中获取用户提交的所有数据,就必须监听 request 事件 的 data 事件(因为每次浏览器提交一部分数据到服务器就会触发一次 data 事件)// 那么,什么时候才表示浏览器把所有数据都提交到服务器了呢?就是当 request 对象的 end 事件被触发的时候。// 监听 request 的对象的 data 事件 和 end 事件代码如下:// 声明一个数组,用来保存用户每次提交过来的数据var array = [];req.on('data', function (chunk) {// 此处的 chunk 参数,就是浏览器本次提交过来的一部分数据// chunk 的数据类型是 Buffer(chunk就是一个Buffer对象)array.push(chunk);});// 监听 request 对象的 end 事件// 当 end 事件被触发的时候,表示上所有数据都已经提交完毕了req.on('end', function () {// console.log(postBody);  // title=OneFine%E7%9A%84%E5%85%A8%E6%A0%88%E6%8A%80%E6%9C%AF%E5%8D%9A%E5%AE%A2&url=ooooooooooooooooo&text=fffffffffffffffff// 在这个事件中只要把 array 中的所有数据汇总起来就好了// 把 array 中的每个 buffer 对象,集合起来转换为一个 buffer 对象// title=fffffff&url=ffffff&text=ffffff// {title: 'fffff', url: 'fffff', text: 'ffffff'}// JSON.parse();var postBody = Buffer.concat(array);// console.log(postBody);// 把 获取到的 buffer 对象转换为一个字符串postBody = postBody.toString('utf8');// 把 post 请求的查询字符串,转换为一个 json 对象postBody = querystring.parse(postBody);// console.log(postBody);// 将用户提交的新闻 push 到 list 中list.push(postBody);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {if (err)throw err;res.statusCode = 302;res.statusMessage = 'Found';res.setHeader('Location', '/');res.end();});});});} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.gifres.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});

4、实现首页显示新闻列表

underscore模块介绍:https://blog.csdn.net/jiduochou963/article/details/104154674

安装:npm install underscore --save

用法:

var _ = require('underscore');// 声明了一段代码模板代码的 html 模板
var html = '<h2><%= name %></h2>';// template() 函数的返回依然是一个函数
var fn = _.template(html);// 调用template()返回的函数fn()
// fn 接收一个数据对象,并用该数据对象将html中的模板内容替换,生成最终的html代码
html = fn({name: "one fine"});console.log(html);console.log('-----');console.log(fn.toString());

执行结果:

D:\NodeJSProject\hackernews\underscoredemo>node index.js
<h2>one fine</h2>
-----
function(data) {return render.call(this, data, _);}D:\NodeJSProject\hackernews\underscoredemo>

代码如下:

// 当前项目(包) 的入口文件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');// 2. 创建服务
http.createServer(function (req, res) {// 通过 url 模块,调用 url.parse() 方法解析用户请求的 url(req.url)var urlObj = url.parse(req.url, true);// console.log(urlObj);// 为 res 对象添加一个 render() 函数,方便后续使用// 因为现在要渲染的 index.html 中需要用到模板数据, 所以给 render 函数增加了第二个参数tplData// 第二个参数的作用就是用来传递 html 页面中要使用的模板数据res.render = function (filename, tplData) {/*** 渲染普通文件不必传第二个参数tplData*/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;}// 如果用户传递了模板数据tplData,那么就使用 underscore 的 template 方法进行替换// 如果用户没有传递 模板数据,那么就不进行替换if (tplData) {data = _.template(data.toString('utf8'))(tplData);  // 先将buffer对象转成字符串,再调用返回的函数// 等价于:// var fn = _.template(data.toString('utf8'));// data = fn(tplData)}res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();// 先根据用户请求的路径(路由),将对应的HTML页面显示出来if (req.url === '/' || req.url === '/index' && req.method === 'get') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function(err, data) {if (err && err.code !== 'ENOENT') {throw err;}var list_news = JSON.parse(data || '[]');// 2. 在服务器端使用模板引擎,将 list 中的数据和 index.html 文件中的内容结合 渲染给客户端res.render(path.join(__dirname, 'views', 'index.html'), {list: list_news});});} else if (req.url === '/submit' && req.method === 'get') {// 读取 submit.html 并返回res.render(path.join(__dirname, 'views', 'submit.html'));} else if (req.url === '/item' && req.method === 'get') {// 读取 details.html 并返回res.render(path.join(__dirname, 'views', 'details.html'));} else if (req.url.startsWith('/add') && req.method === 'get') {// /add?title=aaaaaaaaaaa&url=http%3A%2F%2Flocalhost%3A9090%2Fsubmit&text=bbbbbbbbbbbbb// 表示 get 方法提交一条新闻// 要获取用户 get 提交的数据,需要用到 url 模块(这个模块是node.js 内置模块,不是第三方模块)// 既然是 get 提交数据,所以通过 req.url 就可以直接获取这些数据,但是这样使用起来不方便(得自己去截取字符串,然后获取想要的数据)// 通过 url 模块,可以将用户 get 提交的数据解析成一个 json 对象,使用起来很方便// console.log(req.url);// res.end('over');// 1. 获取用户 get 提交过来的新闻数据// urlObj.query.title// urlObj.query.url// urlObj.query.text// 1.1 读取 data.json 文件中的数据,并将读取到的数据转换为一个数组// 此处,读取文件的时候可以直接写一个 utf8 编码,这样的话,回调函数中的 data 就是一个字符串了fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {// 因为第一次访问网站, data.json 文件本身就不存在,所以肯定是有错误的// 但是这种错误,我们并不认为是网站出错了,所以不需要抛出异常if (err & err.code !== 'Enoent')  // 不是文件不存在错误throw err;// 如果读取到数据(data.json文件存在),那么就把读取到的数据 data,转换为 list数组// 如果没有读取到数据,那么就把 '[]' 转换为数组var list = JSON.parse(data || '[]');  // 将string转换为json对象// 向数组对象 list 中 push 一条新添加的新闻list.push(urlObj.query);// 2. 把用户提交的新闻数据保存到 data.json 文件中fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {// JSON.stringify(list) 从一个对象解析出字符串if (err)throw err;// console.log("ok");// 设置响应报文头,通过响应报文头告诉浏览器,执行一次页面跳转操作// 3. 跳转到新闻列表页——重定向res.statusCode = 302;  // 3开头表示跳转;301表示页面永久移动到某个位置,302表示临时移动res.statusMessage = 'Found';res.setHeader('Location', '/');  // 服务器告诉浏览器跳转位置res.end();  // 结束响应})})// res.end(req.url);} else if (req.url === '/add' && req.method === 'post') {// 注意这里为post提交方式,使用req.url为'/add'即可// 表示 post 方法提交一条新闻// 1. 读取 data.json 文件中的数据fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'ENOENT')throw err;var list = JSON.parse(data || '[]');// 2. 获取用户 post 提交的数据// 因为 post 提交数据的时候,数据量可能比较大,所以会分多次进行提交,每次提交一部分数据// 此时要想在服务器中获取用户提交的所有数据,就必须监听 request 事件 的 data 事件(因为每次浏览器提交一部分数据到服务器就会触发一次 data 事件)// 那么,什么时候才表示浏览器把所有数据都提交到服务器了呢?就是当 request 对象的 end 事件被触发的时候。// 监听 request 的对象的 data 事件 和 end 事件代码如下:// 声明一个数组,用来保存用户每次提交过来的数据var array = [];req.on('data', function (chunk) {// 此处的 chunk 参数,就是浏览器本次提交过来的一部分数据// chunk 的数据类型是 Buffer(chunk就是一个Buffer对象)array.push(chunk);});// 监听 request 对象的 end 事件// 当 end 事件被触发的时候,表示上所有数据都已经提交完毕了req.on('end', function () {// console.log(postBody);  // title=OneFine%E7%9A%84%E5%85%A8%E6%A0%88%E6%8A%80%E6%9C%AF%E5%8D%9A%E5%AE%A2&url=ooooooooooooooooo&text=fffffffffffffffff// 在这个事件中只要把 array 中的所有数据汇总起来就好了// 把 array 中的每个 buffer 对象,集合起来转换为一个 buffer 对象// title=fffffff&url=ffffff&text=ffffff// {title: 'fffff', url: 'fffff', text: 'ffffff'}// JSON.parse();var postBody = Buffer.concat(array);// console.log(postBody);// 把 获取到的 buffer 对象转换为一个字符串postBody = postBody.toString('utf8');// 把 post 请求的查询字符串,转换为一个 json 对象postBody = querystring.parse(postBody);// console.log(postBody);// 将用户提交的新闻 push 到 list 中list.push(postBody);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {if (err)throw err;res.statusCode = 302;res.statusMessage = 'Found';res.setHeader('Location', '/');res.end();});});});} else if (req.url.startsWith('/resources') && req.method === 'get') {// 如果用户请求是以 /resources 开头,并且是 get 请求,就认为用户是要请求静态资源// /resources/images/s.gifres.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});

5、 显示新闻详情

5.1 添加新闻时增加一个id属性

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.createServer(function (req, res) {var urlObj = url.parse(req.url, true);res.render = function (filename, tplData) {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 (tplData)data = _.template(data.toString('utf8'))(tplData);  // 先将buffer对象转成字符串,再调用返回的函数res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();if (req.url === '/' || req.url === '/index' && req.method === 'get') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function(err, data) {if (err && err.code !== 'ENOENT')throw err;var list_news = JSON.parse(data || '[]');res.render(path.join(__dirname, 'views', 'index.html'), {list: list_news});});} else if (req.url === '/submit' && req.method === 'get') {res.render(path.join(__dirname, 'views', 'submit.html'));} else if (req.url === '/item' && req.method === 'get') {res.render(path.join(__dirname, 'views', 'details.html'));} else if (req.url.startsWith('/add') && req.method === 'get') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'Enoent')throw err;var list = JSON.parse(data || '[]');// 在把 新闻 添加到 list 之前,为新闻增加一个 id 属性// 注意这种写法并不严谨,当删除新闻的时候会出现id重复的问题,偷懒偷懒urlObj.query.id = list.length;list.push(urlObj.query);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {if (err)throw err;res.statusCode = 302;res.statusMessage = 'Found';res.setHeader('Location', '/');res.end();})})} else if (req.url === '/add' && req.method === 'post') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'ENOENT')throw err;var list = JSON.parse(data || '[]');var array = [];req.on('data', function (chunk) {array.push(chunk);});req.on('end', function () {var postBody = Buffer.concat(array);postBody = postBody.toString('utf8');postBody = querystring.parse(postBody);// 在把 新闻 添加到 list 之前,为新闻增加一个 id 属性// 注意这种写法并不严谨,当删除新闻的时候会出现id重复的问题,偷懒偷懒postBody.id = list.length;list.push(postBody);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {if (err)throw err;res.statusCode = 302;res.statusMessage = 'Found';res.setHeader('Location', '/');res.end();});});});} else if (req.url.startsWith('/resources') && req.method === 'get') {res.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});
5.2 新闻详情的完整代码

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.createServer(function (req, res) {var urlObj = url.parse(req.url, true);res.render = function (filename, tplData) {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 (tplData)data = _.template(data.toString('utf8'))(tplData);  // 先将buffer对象转成字符串,再调用返回的函数res.setHeader('Content-Type', mime.getType(filename));res.end(data);});};req.url = req.url.toLowerCase();req.method = req.method.toLowerCase();if (req.url === '/' || req.url === '/index' && req.method === 'get') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'ENOENT')throw err;var list_news = JSON.parse(data || '[]');res.render(path.join(__dirname, 'views', 'index.html'), { list: list_news });});} else if (req.url === '/submit' && req.method === 'get') {res.render(path.join(__dirname, 'views', 'submit.html'));} else if (urlObj.pathname === '/item' && req.method === 'get') {// 或者req.url.startsWith('/item')// 1. 获取当前用户请求的新闻的 id// urlObj.query.id // 2. 读取 data.json 文件中的数据,根据 id 找到对应新闻fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'ENOENT')throw err;// 读取到的新闻数据var list_news = JSON.parse(data || '[]');var model = null;// 循环 list_news 中的数据,找到和 id 值相等的数据for (var i = 0; i < list_news.length; i++) {// 判断集合中是否有与用户提交的 id 相等的新闻if (list_news[i].id.toString() === urlObj.query.id) {// 如果找到了相等的新闻,则将其记录下来model = list_news[i];break;}}if (model) {// 3. 调用 res.render() 函数进行模板引擎的渲染res.render(path.join(__dirname, 'views', 'details.html'), { item: model });} else {res.end('No Such Item');}});} else if (req.url.startsWith('/add') && req.method === 'get') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'Enoent')throw err;var list = JSON.parse(data || '[]');// 在把 新闻 添加到 list 之前,为新闻增加一个 id 属性// 注意这种写法并不严谨,当删除新闻的时候会出现id重复的问题,偷懒偷懒urlObj.query.id = list.length;list.push(urlObj.query);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {if (err)throw err;res.statusCode = 302;res.statusMessage = 'Found';res.setHeader('Location', '/');res.end();})})} else if (req.url === '/add' && req.method === 'post') {fs.readFile(path.join(__dirname, 'data', 'data.json'), 'utf8', function (err, data) {if (err && err.code !== 'ENOENT')throw err;var list = JSON.parse(data || '[]');var array = [];req.on('data', function (chunk) {array.push(chunk);});req.on('end', function () {var postBody = Buffer.concat(array);postBody = postBody.toString('utf8');postBody = querystring.parse(postBody);// 在把 新闻 添加到 list 之前,为新闻增加一个 id 属性// 注意这种写法并不严谨,当删除新闻的时候会出现id重复的问题,偷懒偷懒postBody.id = list.length;list.push(postBody);fs.writeFile(path.join(__dirname, 'data', 'data.json'), JSON.stringify(list), function (err) {if (err)throw err;res.statusCode = 302;res.statusMessage = 'Found';res.setHeader('Location', '/');res.end();});});});} else if (req.url.startsWith('/resources') && req.method === 'get') {res.render(path.join(__dirname, req.url));} else {res.writeHead(404, 'Not Found', {'Content-Type': 'text/html; charset=utf-8'});res.end('404, Page Not Found.');}}).listen(8080, function () {console.log('服务启动成功,请访问: http://127.0.0.1:8080 .')
});

6、其他文件代码

6.1 views\index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>index</title><link rel="icon" href="../resources/images/logo.png">
</head><body><center><table id="hnmain" border="0" cellpadding="0" cellspacing="0" width="85%"><tbody><!-- 头部start --><tr><td><table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding:2px"><tbody><tr><td style="line-height:12pt; height:10px;"><span class="pagetop"><b class="hnname"><a href="/">Hacker News主页</a></b><a href="/submit">提交</a></span></td></tr></tbody></table></td></tr><tr style="height:10px"></tr><!-- 头部end --><!-- 中间新闻列表部分start --><tr><td><table border="0" cellpadding="0" cellspacing="0" class="itemlist"><% for (var i = 0; i < list.length; i++) { %><tr><td align="right" valign="top" class="title"><span class="rank"><%= (i+1) %>.</span> <!--新闻编号--></td><td class="title"><a href="/item?id=<%= list[i].id %>"><%= list[i].title %> <!--新闻标题--></a>(<%= list[i].url %>) <!--新闻链接--></td></tr><tr class="spacer" style="height:5px"></tr><% } %></table></td></tr><!-- 中间新闻列表部分end --></tbody></table></center></body></html>
6.2 views\details.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title><%= item.title %> | Hacker News</title>
</head><body><center><table><tr class="athing" id="13720271"><td class="title"><a href=<%= item.url %>><%= item.title %></a></td><td><p><%= item.text %></p></td></tr></table></center></body></html>
6.3 views\submit.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>submit</title>
</head><body><center><table border="0" cellpadding="0" cellspacing="0" width="85%"><tr> <td style="line-height:12pt; height:10px;"><b>Submit</b></td></tr><tr><td><form method="post" action="/add"><table border="0"><tr><td>title</td><td><input type="text" name="title" value="" size="50" /></td></tr><tr><td>url</td><td><input type="text" name="url" value="" size="50" /></td></tr><tr><td></td><td><b>or</b></td></tr><tr><td>text</td><td><textarea name="text" rows="4" cols="49"></textarea></td></tr><tr><td></td><td><input type="submit" value="submit" /></td></tr></table></form></td></tr></table></center></body></html>

目录结构:

hackernews> tree /f
D:.
│  app.js
│  package-lock.json
│  package.json
│
├─data
│      data.json
│
├─node_modules
...
│
├─resources
│  ├─css
│  └─images
│          logo.png
│
└─viewsdetails.htmlindex.htmlsubmit.htmlhackernews>

案例一:NodeJS搭建一个新闻发布系统相关推荐

  1. 一个完整的新闻发布系统

    一个完整的新闻发布系统 关键字:JSP.Java.新闻发布 环境:Tomcat 5.5.7 + J2SE 5.0 + PostgreSQL 8.0 我将分几个步骤完成对一个新闻发布系统的构建,来理解J ...

  2. 【计算机毕业设计】新闻发布系统

    一.系统截图(需要演示视频可以私聊) 摘  要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们 ...

  3. asp毕业设计——基于asp+access的新闻发布系统设计与实现(毕业论文+程序源码)——新闻发布系统

    基于asp+access的新闻发布系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于asp+access的新闻发布系统设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦.需要下载开 ...

  4. 新闻发布系统之浅谈分页技术

    我们今天生活在一个大数据时代,数据量成指数增长.在我们的网页检索数据是往往会检索到太多的数据,但是我们的网页大小又是有限的不能一次性把所有的数据都显示出来,为此分页技术的诞生变得尤为的重要.我在开发一 ...

  5. 14.4 设计新闻发布系统

    14.4  设计新闻发布系统 上面对环境配置完毕.在开始编码之前,先来设计新闻发布系统,包括设计页面.设计业务逻辑和设计数据库. 14.4.1  设计页面 为了示例方便,这里的页面都没有使用图片. 从 ...

  6. 【毕业设计】【期末作业】新闻发布系统(php+mysql)

    分享一个新闻发布系统网站,后台主要是采用php的thinkphp框架制做的,数据库采用mysql进行处理 这个系统主要分为用户模块和后台管理模块,具体功能有 用户模块:注册,登陆,查看个人信息,修改个 ...

  7. 新闻发布系统的设计与实现

    摘要 伴随着网络的出现,网页逐渐融入人们的生活.快速及时的新闻浏览,五彩缤纷的网上信息,使网络与人们的生活息息相关.它打破了地域限制,真正使信息得以共享,改变了人们的工作和生活方式.网站新闻发布系统, ...

  8. JavaWeb新闻发布系统的登录新闻增加

    目录 一.登陆 登陆界面代码: 登陆逻辑处理代码: 二.首页代码 三.新闻添加 增加界面的代码: 处理增加的代码: 数据库代码 前言:我已经和大家分享一些关于JavaWeb的简单知识,我会运用这些写一 ...

  9. 【NodeJS 学习笔记04】新闻发布系统

    前言 昨天,我们跟着这位大哥的博客(https://github.com/nswbmw/N-blog/wiki/_pages)进行了nodeJS初步的学习,最后也能将数据插入数据库了 但是一味的跟着别 ...

最新文章

  1. JVM结构、内存分配、垃圾回收算法、垃圾收集器。
  2. 从输入 URL 到页面加载完成的过程中都发生了什么
  3. 工作中Oracle常用的SQL
  4. 如何用互联网上的广告来赚取广告费——有点吹牛吗?
  5. hdfs数据均衡操作命令
  6. 操作系统(5) -- 输入/输出管理
  7. Field EXP_REVENUE
  8. Java 的日子屈指可数,这是真的吗?
  9. 【转】3.3SharePoint服务器端对象模型 之 访问文件和文件夹(Part 3)
  10. stm32f103移植到f0_STM32F042替换STM32F103
  11. mac wmware 无网络_无线网络中常用的技术名词
  12. 电路 晶振频率_都说晶振是电路的心脏,你真的了解它吗?
  13. 乾坤大挪移,巧迁数据到proxmox平台
  14. nyoj 239 月老的难题
  15. android 缩放组件,Android控件之ZoomControls缩放控件
  16. 通过xmlhttp实现-报告归档
  17. 苹果系统下怎么设置iChat 登陆MSN?
  18. 绝版| 张小龙《微信背后的产品观》纸质书!送3本......
  19. win11拉伸屏幕_win11系统出现拉伸屏幕问题修复办法
  20. cpu插槽类型 产品参数解释

热门文章

  1. 一个老程序猿的焦虑3
  2. 文献精读——(第十四篇)目标检测综述
  3. P2P 中的 NAT 穿越(打洞)方案详解
  4. iOS 开发:绘制像素到屏幕
  5. 视频语音聊天系统的漏洞
  6. 大众点评 mtgsig 安卓逆向 研究
  7. loadrunner中浏览器缓存设置
  8. 80211-pcap包类型有3种link type
  9. 昨天用了下Netty,发现真香!
  10. ceiling 和 floor 函数