注:需要安装的包上次已经安装过了
npm install -g express npm install -g nodejieba以下是对于助教代码的理解、修改、添加后自己运行的产物
对mysql进行了定义确认
module.exports = { mysql: { host: '127.0.0.1', user: 'root', password: 'root', database:'test', // 最大连接数,默认为10 connectionLimit: 10 } }; //127.0.0.1代表本地的mysql
图中是上次作业中爬虫cmd的运行过程以及进入数据库的运行图。
接着,在mysql中创建三个表单,分别记录用户数据(用户ID、昵称、密码(MD5)、权限、账户余额等信息)、新闻数据(URL、标题、内容、编辑、来源等信息)以及用户具体行为(登录、查询等操作):
--之前的新闻数据表 CREATE TABLE `fetches` ( `id_fetches` int(11) NOT NULL AUTO_INCREMENT, `url` varchar(200) DEFAULT NULL, `source_name` varchar(200) DEFAULT NULL, `source_encoding` varchar(45) DEFAULT NULL, `title` varchar(200) DEFAULT NULL, `keywords` varchar(200) DEFAULT NULL, `author` varchar(200) DEFAULT NULL, `publish_date` date DEFAULT NULL, `crawltime` datetime DEFAULT NULL, `content` longtext, `createtime` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id_fetches`), UNIQUE KEY `id_fetches_UNIQUE` (`id_fetches`), UNIQUE KEY `url_UNIQUE` (`url`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --创建用户信息数据表 CREATE TABLE `crawl`.`user` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `username` VARCHAR(45) NOT NULL, `password` VARCHAR(45) NOT NULL, `registertime` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `username_UNIQUE` (`username`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; --记录用户的登陆,查询(具体查询语句)操作 CREATE TABLE `crawl`.`user_action` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `username` VARCHAR(45) NOT NULL, `request_time` VARCHAR(45) NOT NULL, `request_method` VARCHAR(20) NOT NULL, `request_url` VARCHAR(300) NOT NULL, `status` int(4), `remote_addr` VARCHAR(100) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;创造一个调用数据的路径(路由),来让网页与服务器连接。以下是调用搜索新闻数据、图表的路径:
var newsDAO = require('../dao/newsDAO'); var express = require('express'); var router = express.Router(); router.get('/search', function(request, response) { console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var param = request.query; newsDAO.search(param,function (err, result, fields) { response.json({message:'data',result:result}); }) } }); router.get('/histogram', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var fetchSql = "select publish_date as x,count(publish_date) as y from fetches group by publish_date order by publish_date;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:result})); response.end(); }); } }); router.get('/pie', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var fetchSql = "select author as x,count(author) as y from fetches group by author;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:result})); response.end(); }); } }); router.get('/line', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var keyword = '疫情'; //也可以改进,接受前端提交传入的搜索词 //var fetchSql = "select content,publish_date from fetches where content like'%" + keyword + "%' order by publish_date;"; var fetchSql = "select count(1) as y,publish_date as x from fetches where content like'%" + keyword + "%' group by publish_date order by publish_date;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:result})); response.end(); }); } }); router.get('/wordcloud', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var fetchSql = "select content from fetches;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:result}));//返回处理过的数据 response.end(); }); } }); module.exports = router;然后再是调用用户信息的路径(登录、注册、退出登录):
var express = require('express'); var router = express.Router(); var userDAO = require('../dao/userDAO'); router.post('/login', function(req, res) { var username = req.body.username; var password = req.body.password; // var sess = req.session; userDAO.getByUsername(username, function (user) { if(user.length==0){ res.json({msg:'用户不存在!请检查后输入'}); }else { if(password===user[0].password){ req.session['username'] = username; res.cookie('username', username); res.json({msg: 'ok'}); // res.json({msg:'ok'}); }else{ res.json({msg:'用户名或密码错误!请检查后输入'}); } } }); }); /* add users */ router.post('/register', function (req, res) { var add_user = req.body; // 先检查用户是否存在 userDAO.getByUsername(add_user.username, function (user) { if (user.length != 0) { // res.render('index', {msg:'用户不存在!'}); res.json({msg: '用户已存在!'}); }else { userDAO.add(add_user, function (success) { res.json({msg: '成功注册!请登录'}); }) } }); }); // 退出登录 router.get('/logout', function(req, res, next){ // 备注:这里用的 session-file-store 在destroy 方法里,并没有销毁cookie // 所以客户端的 cookie 还是存在,导致的问题 --> 退出登陆后,服务端检测到cookie // 然后去查找对应的 session 文件,报错 // session-file-store 本身的bug req.session.destroy(function(err) { if(err){ res.json('退出登录失败'); return; } // req.session.loginUser = null; res.clearCookie('username'); res.json({result:'/index.html'}); }); }); module.exports = router;以下是关于四个Echarts图表的制作
$scope.histogram = function () { $scope.isShow = false; $http.get("/news/histogram") .then( function (res) { if(res.data.message=='url'){ window.location.href=res.data.result; }else { // var newdata = washdata(data); let xdata = [], ydata = [], newdata; var pattern = /\d{4}-(\d{2}-\d{2})/; res.data.result.forEach(function (element) { // "x":"2020-04-28T16:00:00.000Z" ,对x进行处理,只取 月日 xdata.push(pattern.exec(element["x"])[1]); ydata.push(element["y"]); }); newdata = {"xdata": xdata, "ydata": ydata}; var myChart = echarts.init(document.getElementById('main1')); // 指定图表的配置项和数据 var option = { title: { text: '新闻发布数 随时间变化' }, tooltip: {}, legend: { data: ['新闻发布数'] }, xAxis: { data: newdata["xdata"] }, yAxis: {}, series: [{ name: '新闻数目', type: 'bar', data: newdata["ydata"] }] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); } }, function (err) { $scope.msg = err.data; }); }; $scope.pie = function () { $scope.isShow = false; $http.get("/news/pie").then( function (res) { if(res.data.message=='url'){ window.location.href=res.data.result; }else { let newdata = []; //console.log(res.data.result); var pattern = /责任编辑:(.+)/;//匹配名字 var i=0; res.data.result.forEach(function (element) { // "x": 责任编辑:李夏君 ,对x进行处理,只取 名字 //newdata.push({name: pattern.exec(element["x"])[1], value: element["y"]}); newdata.push({name: element["x"].replace("责任编辑:",''), value: element["y"]}); }); console.log(newdata); var myChart = echarts.init(document.getElementById('main1')); var app = {}; option = null; // 指定图表的配置项和数据 var option = { title: { text: '作者发布新闻数量', x: 'center' }, tooltip: { trigger: 'item', formatter: "{a} <br/>{b} : {c} ({d}%)" }, legend: { orient: 'vertical', left: 'left', // data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎'] }, series: [ { name: '访问来源', type: 'pie', radius: '55%', center: ['50%', '60%'], data: newdata, itemStyle: { emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] }; // myChart.setOption(option); app.currentIndex = -1; setInterval(function () { var dataLen = option.series[0].data.length; // 取消之前高亮的图形 myChart.dispatchAction({ type: 'downplay', seriesIndex: 0, dataIndex: app.currentIndex }); app.currentIndex = (app.currentIndex + 1) % dataLen; // 高亮当前图形 myChart.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: app.currentIndex }); // 显示 tooltip myChart.dispatchAction({ type: 'showTip', seriesIndex: 0, dataIndex: app.currentIndex }); }, 1000); if (option && typeof option === "object") { myChart.setOption(option, true); } ; } }); }; $scope.line = function () { $scope.isShow = false; $http.get("/news/line").then( function (res) { if(res.data.message=='url'){ window.location.href=res.data.result; }else { var myChart = echarts.init(document.getElementById("main1")); var xx=[]; var yy=[]; res.data.result.forEach(function (element) { //newdata.push({name: element["x"].replace("责任编辑:",''), value: element["y"]}); xx.push( element["x"]); yy.push( element["y"]); }); console.log(xx); console.log(yy); option = { title: { text: '"疫情"该词在新闻中的出现次数随时间变化图' }, xAxis: { type: 'category', data: xx//Object.keys(res.data.result) }, yAxis: { type: 'value' }, series: [{ data: yy,//Object.values(res.data.result), type: 'line', itemStyle: {normal: {label: {show: true}}} }], }; if (option && typeof option === "object") { myChart.setOption(option, true); } } }); }; $scope.wordcloud = function () { $scope.isShow = false; $http.get("/news/wordcloud").then( function (res) { if(res.data.message=='url'){ window.location.href=res.data.result; }else { var mainContainer = document.getElementById('main1'); var chart = echarts.init(mainContainer); var data = []; for (var name in res.data.result) { data.push({ name: name, value: Math.sqrt(res.data.result[name]) }) } var maskImage = new Image(); maskImage.src = './images/logo.png'; var option = { title: { text: '所有新闻内容 jieba分词 的词云展示' }, series: [{ type: 'wordCloud', sizeRange: [12, 60], rotationRange: [-90, 90], rotationStep: 45, gridSize: 2, shape: 'circle', maskImage: maskImage, drawOutOfBound: false, textStyle: { normal: { fontFamily: 'sans-serif', fontWeight: 'bold', // Color can be a callback function or a color string color: function () { // Random color return 'rgb(' + [ Math.round(Math.random() * 160), Math.round(Math.random() * 160), Math.round(Math.random() * 160) ].join(',') + ')'; } }, emphasis: { shadowBlur: 10, shadowColor: '#333' } }, data: data }] }; maskImage.onload = function () { // option.series[0].data = data; chart.clear(); chart.setOption(option); }; window.onresize = function () { chart.resize(); }; } }); } });为了方便图表的展示,在这里需要引入nodejieba包(jiaba需要c++,python,node-gyp,make环境支持),以下是c++环境的建构
yum -y install gcc yum -y install gcc-c++首先是登陆页面的界面设置(包括注册和登录),代码如下:
<!DOCTYPE html> <html ng-app="login"> <head> <meta charset="utf-8" /> <title>Login</title> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css"> <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"></script> <script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script> <!-- <script src="../node_modules/angular/angular.min.js"></script>--> <script src="/angular/angular.min.js"></script> <!-- 引入自己的样式与js--> <link rel="stylesheet" type="text/css" href="stylesheets/index.css"> <script type="text/javascript" src="javascripts/index.js"></script> <script> var app = angular.module('login', []); app.controller('loginCtrl', function ($scope, $http, $timeout) { // 登录时,检查用户输入的账户密码是否与数据库中的一致 $scope.check_pwd = function () { var data = JSON.stringify({ username: $scope.username, password: $scope.password }); $http.post("/users/login", data) .then( function (res) { if(res.data.msg=='ok') { window.location.href='/news.html'; }else{ $scope.msg=res.data.msg; } }, function (err) { $scope.msg = err.data; }); }; //增加注册用户 $scope.doAdd = function () { // 检查用户注册时,输入的两次密码是否一致 if($scope.add_password!==$scope.confirm_password){ // $timeout(function () { // $scope.msg = '两次密码不一致!'; // },100); $scope.msg = '两次密码不一致!'; } else { var data = JSON.stringify({ username: $scope.add_username, password: $scope.add_password }); $http.post("/users/register", data) .then(function (res) { if(res.data.msg=='成功注册!请登录') { $scope.msg=res.data.msg; $timeout(function () { window.location.href='index.html'; },2000); } else { $scope.msg = res.data.msg; } }, function (err) { $scope.msg = err.data; }); } }; }); </script> </head> <body> <div class="container" ng-controller="loginCtrl"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <div class="panel panel-login"> <div class="panel-heading"> <div class="row"> <div class="col-xs-6"> <a href="#" class="active" id="login-form-link">Login</a> </div> <div class="col-xs-6"> <a href="#" id="register-form-link">Register</a> </div> </div> <hr> </div> <div class="panel-body"> <div class="row"> <div class="col-lg-12"> <form id="login-form" method="post" role="form" style="display: block;"> <!-- 登陆部分--> <div class="form-group"> <input ng-model="username" tabindex="1" class="form-control" placeholder="Username" value=""/> </div> <div class="form-group"> <input type="password" ng-model="password" tabindex="2" class="form-control" placeholder="Password"> </div> <!-- <div class="form-group text-center">--> <!-- <input type="checkbox" tabindex="3" class="" name="remember" id="remember">--> <!-- <label for="remember"> Remember Me</label>--> <!-- </div>--> <div class="form-group"> <div class="row"> <div class="col-sm-6 col-sm-offset-3"> <button id="login-submit" tabindex="4" class="form-control btn btn-login" ng-click="check_pwd()">LOG IN</button> </div> </div> </div> </form> <form id="register-form" method="post" role="form" style="display: none;"> <div class="form-group"> <input ng-model="add_username" tabindex="1" class="form-control" placeholder="Username" value=""/> </div> <div class="form-group"> <input type="password" ng-model="add_password" tabindex="2" class="form-control" placeholder="Password"> </div> <div class="form-group"> <input type="password" ng-model="confirm_password" tabindex="2" class="form-control" placeholder="Confirm Password"> </div> <div class="form-group"> <div class="row"> <div class="col-sm-6 col-sm-offset-3"> <button tabindex="4" class="form-control btn btn-register" ng-click="doAdd()">Register Now</button> </div> </div> </div> </form> </div> </div> </div> <!-- <div class="alert alert-warning alert-dismissible fade show">--> <!-- <button type="button" class="close" data-dismiss="alert">×</button>--> <!-- <strong>警告!</strong>{{msg}}--> <!-- </div>--> </div> <div class="alert alert-warning" ng-if="msg && msg!='ok'"> <a href="#" class="close" data-dismiss="alert">×</a> <strong>警告!</strong>{{msg}} </div> </div> </div> </div> </body>接下来是新闻页面的设置,包括图表以及退出登录的操作:
<html ng-app="news"> <head> <meta charset="utf-8"> <title>News</title> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script> <script src="http://cdn.bootcss.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/dist/echarts.min.js"></script> <script src='javascripts/dist/echarts-wordcloud.min.js'></script> <script src="/angular/angular.min.js"></script> <script src="javascripts/news.js" type="text/javascript"></script> </head> <body ng-controller="news_Ctrl" ng-init="isShow=false"> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">News</a> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li ><a ng-click="showSearch()">检索</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">图片<span class="caret"></span></a> <ul class="dropdown-menu"> <li><a ng-click="histogram()">柱状图</a></li> <li><a ng-click="pie()">饼状图</a></li> <li><a ng-click="line()">折线图</a></li> <li><a ng-click="wordcloud()">词云</a></li> </ul> </li> <li> <a href="#" class="dropdown-toggle" data-toggle="dropdown">账号管理<span class="caret"></span></a> <ul class="dropdown-menu"> <li class="dropdown-header">账号</li> <li><a ng-click="logout()">退出登录</a></li> </ul> </li> </ul> </div> </div> </nav> <!-- 所有的图片都绘制在main1位置--> <span ng-hide="isShow" id="main1" style="width: 1000px;height:600px;position:fixed; top:70px;left:80px"></span> <div ng-show="isShow" style="width: 1300px;position:relative; top:70px;left: 80px"> <!-- 查询页面--> <div ng-include="'search.html'"></div> </div> </body> </html>然后再编写一个搜索新闻的界面,包括标题与内容的关键字查找:
<form class="form-horizontal" role="form"> <div class="row" style="margin-bottom: 10px;"> <label class="col-lg-2 control-label">标题关键字</label> <div class="col-lg-3"> <input type="text" class="form-control" placeholder="标题关键字" ng-model="$parent.title1"> </div> <div class="col-lg-1"> <select class="form-control" autocomplete="off" ng-model="$parent.selectTitle"> <option selected="selected">AND</option> <option>OR</option> </select> </div> <div class="col-lg-3"> <input type="text" class="form-control" placeholder="标题关键字" ng-model="$parent.title2"> </div> </div> <div class="row" style="margin-bottom: 10px;"> <label class="col-lg-2 control-label">内容关键字</label> <div class="col-lg-3"> <input type="text" class="form-control" placeholder="内容关键字" ng-model="$parent.content1"> </div> <div class="col-lg-1"> <select class="form-control" autocomplete="off" ng-model="$parent.selectContent"> <option selected="selected">AND</option> <option>OR</option> </select> </div> <div class="col-lg-3"> <input type="text" class="form-control" placeholder="内容关键字" ng-model="$parent.content2"> </div> </div> <div class="form-group"> <div class="col-md-offset-9"> <button type="submit" class="btn btn-default" ng-click="search()">查询</button> </div> </div> </form> <!--显示查询结果--> <div ng-show="isisshowresult"> <table class="table table-striped"> <thead> <tr> <td>序号</td> <td>标题</td> <td>作者</td> <!-- <td>内容</td>--> <td>关键词</td> <td>链接</td> <td>发布时间</td> </tr> </thead> <tbody> <tr ng-repeat="(key, item) in items"> <td>{{index+key}}</td> <td>{{item.title}}</td> <td>{{item.author}}</td> <!-- <td>{{item.content}}</td>--> <td>{{item.keywords}}</td> <td>{{item.url}}</td> <td>{{item.publish_date}}</td> </tr> </tbody> </table> <div class="row"> <!-- <div class="form-group">--> <div class="pull-left" style="margin-top: 12px;"> <button type="submit" class="btn btn-primary" ng-click="searchsortASC()" >发布时间升序</button> <button type="submit" class="btn btn-primary" ng-click="searchsortDESC()">发布时间降序</button> </div> <!-- </div>--> <div class="pull-right"> <nav> <ul class="pagination"> <li> <a ng-click="Previous()" role="button"><span role="button">上一页</span></a> </li> <li ng-repeat="page in pageList" ng-class="{active:isActivePage(page)}" role="button"> <a ng-click="selectPage(page)" >{{ page }}</a> </li> <li> <a ng-click="Next()" role="button"><span role="button">下一页</span></a> </li> </ul> </nav> </div> </div> </div>这一个代码决定了前面的代码使用的情况
var createError = require('http-errors'); var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var logger = require('morgan'); var logDAO = require('./dao/logDAO.js'); // var fs = require('fs');//加了文件操作的模块 // var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flag: 'a' });//创建一个写文件流,并且保存在当前文件夹的access.log文件中 // var indexRouter = require('./routes/users'); var usersRouter = require('./routes/users'); var newsRouter = require('./routes/news'); var app = express(); //设置session app.use(session({ secret: 'sessiontest',//与cookieParser中的一致 resave: true, saveUninitialized: false, // 是否保存未初始化的会话 cookie : { maxAge : 1000 * 60 * 60, // 设置 session 的有效时间,单位毫秒 }, })); // view engine setup // app.set('views', path.join(__dirname, 'views')); // app.set('view engine', 'ejs'); let method = ''; app.use(logger(function (tokens, req, res) { console.log('打印的日志信息:'); var request_time = new Date(); var request_method = tokens.method(req, res); var request_url = tokens.url(req, res); var status = tokens.status(req, res); var remote_addr = tokens['remote-addr'](req, res); if(req.session){ var username = req.session['username']||'notlogin'; }else { var username = 'notlogin'; } // 直接将用户操作记入mysql中 if(username!='notlogin'){ logDAO.userlog([username,request_time,request_method,request_url,status,remote_addr], function (success) { console.log('成功保存!'); }) } console.log('请求时间 = ', request_time); console.log('请求方式 = ', request_method); console.log('请求链接 = ', request_url); console.log('请求状态 = ', status); console.log('请求长度 = ', tokens.res(req, res, 'content-length'),); console.log('响应时间 = ', tokens['response-time'](req, res) + 'ms'); console.log('远程地址 = ', remote_addr); console.log('远程用户 = ', tokens['remote-user'](req, res)); console.log('http版本 = ', tokens['http-version'](req, res)); console.log('浏览器信息 = ', tokens['user-agent'](req, res)); console.log('用户 = ', username); console.log(' ===============',method); }, )); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/angular', express.static(path.join(__dirname , '/node_modules/angular'))); // app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/news', newsRouter); // 检测是否登录 // app.use(function(req, res, next) { // var url = req.url; // // 判断不拦截的路由 除/之外的都拦截 // if ( url!='/users/login' && !req.session.username) { // res.redirect('/users/login'); // }else { // next(); // } // }); // 这个中间件为什么不起作用啊 // app.use((req, res, next) => { // if (!req.session['username']) { // res.redirect('/'); // } else { // next(); // } // }); // catch 404 and forward to error handler app.use(function(req, res, next) { next(createError(404)); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); // res.render('error'); }); module.exports = app;首页:
查询页面以及查询测试:
图表展示:
这次作业最麻烦的地方就在于我的电脑的数据库安装问题……结果到最后还是没能安装成功,只能跑去朋友家借了她的电脑来试着搞……TvT(以上代码来自对助教的代码进行修改、添加、疯狂熬夜尝试后产物,以我个人目前水平还不足以完整完成这一整个程序,只能边做边问边学……)