node+express项目案例(一):https://blog.youkuaiyun.com/Chris__wang/article/details/90738551
node+express项目案例(二):https://blog.youkuaiyun.com/Chris__wang/article/details/90738794
node+express项目案例(三):https://blog.youkuaiyun.com/Chris__wang/article/details/90744140
项目地址:https://github.com/wangyeky3419/node-app
数据库用的是mysql数据库,表内数据会放在代码中
本项目虽然页面不多,但是包含了基本的数据库建立,查询,修改,删除,插入,路由管理以及artTemplate模板应用,页面登录,cookie登录用户信息存储,md5密码加密等
前面已经介绍了页面的基本样式,下面开始介绍一下代码部分
项目的整体架构如上图
README.md
# bin 项目的启动文件,也可以放其他脚本
# node_modules 项目依赖
# public 存放静态文件(css, js, img)
# routes 路由控制器
# views 视图目录 (暂时没用上)
# template 模板放的位置
# app.js 项目入口及程序启动文件
# package.json 配置信息
由于没用涉及到文件上传,故而static目录暂时也没有用上,目录结构很清晰简单
首先介绍app.js 项目入口 注:文件中有些中间件暂时没用用上,但是后期可能会用上,所以没用删除
1. 安装node环境
2. 根据引入的中间件下载依赖 如npm install express express-static .... --save
var express = require('express');//express框架
var static = require('express-static')//接受一个参数,就是静态资源文件所在的目录,也就是文件读取
var bodyParser = require('body-parser');//能够解析数据类的post,但是对于文件上传类型的post不能解析,所以他有点不好
var multer = require('multer');//解析post文件
var cookieParser = require('cookie-parser');
var cookieSession = require('cookie-session');
//创建一个multer对象,指明文件上传到哪个目录
const multerObj = multer({dest:'./static/upload'})
// var consolidate = require('consolidate');//----------------------
var atrTemplate = require('express-art-template')
const expressRoute = require('express-route');
var path = require('path');//解析文件路径
var favicon = require('serve-favicon');
var server = express();//创建服务
server.listen(8080);
var router = express.Router();
//1.获取前台请求数据
//get
server.use(bodyParser.urlencoded())//解析post数据的req.body数据,不然解析不出来会报错
server.use(multerObj.any())
//2. cookie session
server.use(cookieParser())
server.use(cookieParser());
(function(){
//防止被污染变量,用一个函数包起来
var keys = [];
for(var i = 0; i < 10000; i++){
keys[i]='a_'+Math.random()
}
server.use(cookieSession({
name:'session_id',
keys:keys,
maxAge:20*60*1000//20min后过期
}));
})()
server.use(express.static(__dirname+'/public/'))//将静态文件目录指定到public下
server.use(express.static(__dirname+'/template/'))//将每个页面的静态文件目录指定到template下
//======================模板================================
//将指定的art文件渲染成html(art)文件
server.engine('art',atrTemplate);
//模板文件放在哪,这里的views是固定写法,指定模板文件位置(这里是放在template里)
server.set('views','./template');
//输出什么东西,view engine(写法固定,意思是视图引擎),这里设置的是输出html
server.set('view engine','html');
//======================route路由==========================
//创建route放在routes文件夹中 route必须用use 不能用get
server.use('/',require('./routes/index.js')())//当访问根目录的时候,跳转到routes/index.js文件里的route
// server.use('/login',require('./routes/web/login.js')())//当访问login目录的时候,跳转到web/login.js文件里的route里
代码中注释写的非常清楚了,所以文章里就不过多介绍了
接着是跳转到routes下
其中routes下index.js是主路由,login.js news.js user.js分别是对应页面的路由,
index.js
const express = require('express');
const urlLib = require('url');
module.exports = function(){
var router = express.Router();
//检查登录状态,如更没登录,跳转到登录状态
router.use((req,res,next)=>{//没有登录
if(req.url!='/favicon.ico'){
var pattern = new RegExp("/", "g");
var reqUrl = req.url.replace(pattern,'');
reqUrl = '/'+reqUrl
if(!req.session['admin_id']&&reqUrl!='/login'){
//这里检查如果没有登录(没有admin_id)就会重定向到登录界面
//当登录后,就会设置admin_id
res.redirect('/login');//重定向
//备注,重定向后还是会重新访问这里文件,这时候还会做判断,如果判断失败,走else
}else{
next()
}
}
});
router.get('/',(req,res)=>{
res.render('index.art',{
path:'./user/user.art'
})
})
//当访问login,跳转到login路由,进行登录页面渲染
router.use('/login',require('./login.js')());
//当访问user,跳转到user路由,进行user.art渲染
router.use('/user',require('./user.js')());
//当访问news,跳转到news路由,进行news.art渲染
router.use('/news',require('./news.js')());
return router;
}
在index.js里面检查登录状态时,经常会有其他路径干扰,故而加上了过滤,这里如果有大神有好办法,也希望指教一下,
routes/login.js
const express = require('express');
const mysql = require('mysql');
const utils = require('../public/utils/md5.js')//引入md5
var db = mysql.createPool({
host:'localhost',
user:'root',
password:'9527',
database:'learn'
});
module.exports = function(){
var router = express.Router();
//当请求方式为get的时候,约定为访问页面
router.get('/',(req,res)=>{
res.render('login/login.art',{});
})
//当请求方式为post的时候,约定为登录操作
router.post('/',(req,res)=>{
var username = req.body.userName;
//将获取的密码转成md5加密形式,而且数据库存的也是加密后的,并且有后缀
var password = utils.md5(req.body.password+utils.MD5_SUFFIX);
db.query(`SELECT * FROM admin_table WHERE username='${username}'`,(err,data)=>{
if(err){
res.status(500).send('服务器出错').end()
}else{//成功
if(data.length == 0){
res.status(400).send('该用户不存在').end()
}else{//成功且有值
if(data[0].password == password){//对比的是md5后的值
//给session赋值
req.session['admin_id'] = data[0].ID;
//重定向到首页
res.redirect('/')
}else{
res.status(400).send('密码不正确')
}
}
}
})
})
//注册register
router.post('/register',(req,res)=>{
var username = req.body.userName;
//将获取的密码转成md5加密形式,而且数据库存的也是加密后的,并且有后缀
var password = utils.md5(req.body.password+utils.MD5_SUFFIX);
//注册部分
})
return router;
}
注册部分由于时间问题没写完,这里跟普通的数据库增加数据一样,只不过是增加了一个md5密码加密
路由部分的user.js news.js跟这也一样
下面是模板部分。这里用的是.art扩展名,index.art是首页包含导航部分,其余分别是下属子页面,其中如果js和css代码如果不多,我就直接写在art文件里了,并没有外部引用。
template/login/login.art
<!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">
<!-- 因为这个是login页面,所以每个地址都会默认加上一个login,所以访问页面使用../ -->
<link rel="stylesheet" href="../css/bootstrap.min.css">
<link rel="stylesheet" href="./login.css">
<title>登录</title>
</head>
<body>
<canvas id="can"></canvas>
<div class="box center_middle">
<form class="center_middle loginForm" action="/login" method="post">
<div class="form-group row">
<label for="userName" class="col-sm-2 col-form-label">账号:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="userName" name="userName" placeholder="请输入账号" required>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">密码:</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="gridCheck1">
<label class="form-check-label" for="gridCheck1">
记住密码
</label>
<a href="#">忘记密码!</a>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-8 offset-sm-4">
<button class="btn btn-primary" type="submit">登录</button>
<button type="" class="btn btn-primary" id="register" onclick="return false">注册</button>
</div>
</div>
</form>
<!-- 注册 -->
<form class="center_middle registerForm" action="/login/register" method="post" onsubmit="return false">
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label">账号:</label>
<div class="col-sm-9">
<input type="name" class="form-control" id="userName" name="userName" placeholder="请输入账号" required>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-3 col-form-label">密码:</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
</div>
</div>
<div class="form-group row">
<label for="password2" class="col-sm-3 col-form-label">确认密码:</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="password2" placeholder="请再次输入密码" required>
</div>
</div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<button type="submit" class="btn btn-primary" id="back">返回</button>
<button class="btn btn-primary" id="registerSave">注册</button>
</div>
</div>
</form>
</div>
<script src="../js/jquery.min.js"></script>
<script src="../js/bootstrap.min.js"></script>
<script src="../js/dat.gui.js"></script>
<script src="./login.js"></script>
</body>
</html>
template/index.art 可以对应上面的routes/index.js看
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<title>Chris_Wang</title>
<style>
.main_content {
margin-top:56px;
}
</style>
</head>
<body>
<div class="container" style="height:3000px">
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-primary">
<a class="navbar-brand" href="https://blog.youkuaiyun.com/Chris__wang" target="_blank">Chris_Wang</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active nav_item_">
<a class="nav-link" href="/">用户信息</a>
</li>
<li class="nav-item nav_item_news">
<a class="nav-link" href="news">新闻列表</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
公司简介
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item nav_item_news_about" href="about">关于我们</a>
<a class="dropdown-item" href="#">企业风采</a>
</div>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
<div class="main_content">
<!--根据路由跳转时传递的path数据,确定要加载的是哪个页面文件,后面的$data是传递给子组件的数据,其中routes/news.js文件处传递过data数据-->
{{include $data['path'] $data}}
</div>
</div>
</body>
</html>
<script src="/js/bootstrap.min.js"></script>
<script>
//导航当前页高亮显示
var items = $('.nav-item');
var pathname = window.location.pathname
var pattern = new RegExp("/", "g");
var activeName = pathname.replace(pattern,'');
for(var i = 0; i < items.length; i++){
$(items[i]).removeClass('active')
}
$('.nav_item_'+activeName).addClass('active')
</script>
注:代码中部分类名如mr-sm-2 my-2等是bootstrap4里面新增加的实用性工具类,详见
https://v4.bootcss.com/docs/4.0/getting-started/introduction/
中文文档:https://cloud.tencent.com/developer/doc/1018
未完待续。。。