Node.js配合express框架、ejs、mongodb实现应用 && [使用TypeScript重构]

2次阅读

共计 8850 个字符,预计需要花费 23 分钟才能阅读完成。

一、Node.js 默认使用 commonJs 的模块化方案,TypeScript 默认是 ES6 的模块化方案,两者有本质区别。
1.Node.js 的去寻找引入的依赖时,如果是 Node 自带的模块,比如 fs 文件模块,只需要填写 fs 即可。如果是自己定义的模块,那么需要加入./(使用相对路径),暴露接口使用 exports 或者 module.exports
2.TypeScript 的 import * from url 的引入依赖,需要填写完整的相对路径,否则是找不到模块的, 暴露接口使用 export .
3.Node 中使用 TypeScript 需要下一些包去支持,比如 express 框架这些,还有一些支持内置对象的包:
“dependencies”: {
“@babel/core”: “^7.4.0”,
“@types/core-js”: “^2.5.0”,
“browserify”: “^16.2.3”,
“connect-mongo”: “^2.0.3”,
“cookie-parser”: “^1.4.4”,
“ejs”: “^2.6.1”,
“express”: “^4.16.4”,
“express-session”: “^1.15.6”,
“mongoose”: “^5.4.19”,
“nodemon”: “^1.18.10”,
“sha1”: “^1.1.1”
},
“devDependencies”: {
“@types/express”: “^4.16.1”,
“@types/node”: “^11.11.4”,
“ts-loader”: “^5.3.3”,
“ts-node-dev”: “^1.0.0-pre.32”,
“typescript”: “^3.3.4000”,
“webpack”: “^4.29.6”,
“webpack-cli”: “^3.3.0”
}

‘ 具体还需要什么,可以上网去搜索下 ’

二、入口文件,我们使用 ejs 引擎渲染 (res.render() )
1.Node.js 使用 ejs 渲染的核心技巧是渲染数据的指定
2. 尽量一个渲染数据对象包括所有的渲染内容
3. 一个渲染对象可以有很多个属性,每次 get 请求
时先发送一个空的对象到后端,再根据需求逻辑指定
对象属性和内容,最后还是传输那个对象回来。避免了
传送过多的对象,代码看起来很复杂
4. 渲染数据的位置在渲染的 ejs 文件中的放置,
如果需要样式,可以事先在 HTML 结构中包一层 HTML 结构,然后用 CSS 定义好。

‘ 这是 Node.js 版本 ’

‘// 入口文件使用了两个路由器路由,分别处理 get 和 post 请求逻辑。
即使是同一个路由,但是请求方式不一样,他们的处理逻辑不会冲突 ’
const express = require(‘express’);
const db = require(‘./common/db’);
const app = express();
const uirouter = require(‘./router/uirouter’);
const postrouter = require(‘./router/postrouter’);
app.set(‘views’, ‘views’);
app.set(‘view engine’, ‘ejs’);

db.then(() => {
app.use(uirouter);
app.use(postrouter);
})

app.listen(8080, err => {
if (!err) {
console.log(‘ 端口号监听成功 ’)
} else {
console.log(‘ 端口监听失败 ’, err)
}
})

—————–

‘ 这是 TypeScript 版本 ’

import express from ‘./node_modules/@types/express/index’;
import db from ‘./common/db1’;
import uirouter from ‘./router/uirouter1’;
import postrouter from ‘./router/postrouter1’;
const app: any = express();
app.set(‘views’, ‘views’);
app.set(‘view engine’, ‘ejs’);
db.then((): void => {
app.use(uirouter);
app.use(postrouter);
});
app.listen(8080, (err): void => {
if (!err) {
console.log(‘ 服务器连接成功 ’);
} else {
console.log(‘ 服务器连接成功 ’);
};
});

三、get 请求的路由处理模块
1. 路由模块的核心,一个路由处理一个逻辑
2.res.end / send / render 后面再写逻辑
也不会执行了,因为已经返回响应。
3. 对于 cookie 的使用我们需要依赖第三方中间件
4.res.render() 里面是写 ejs 渲染的文件,所以可以不用写 ejs 的后缀
5.res.redirect() 里面写的是定向的那个路由,指定前往那个路由,
然后根据那个路由的逻辑处理,此时浏览器中的 url 会改变。这就叫重定向
‘// 这里我们使用了第三方中间件处理 cookie 并且
携带数据,大概设计思路:
1. 没有登录过不能进入个人中心,会跳转到登录界面
2. 登录过后会有一个免登录期限进入个人中心
3. 在登录界面可以通过用户名和邮箱找回密码
4. 在 Node 端处理逻辑,只有 res.redirect() 可以
改变浏览器的网址,切记。
5. 每个路由器路由代表每个不同的逻辑
6.get 模块只处理渲染哪个页面的逻辑 ’
const {Router} = require(‘express’);
const model = require(‘../common/model’);
const cookieParse = require(‘cookie-parser’);
const router = new Router();
router.use(cookieParse())
router.get(‘/index’, (req, res) => {
res.render(‘index.ejs’, { err: “”})
})
router.get(‘/’, (req, res) => {
res.redirect(‘/index’);
});
router.get(‘/login’, (req, res) => {
res.render(‘login.ejs’, { err: “”});
});
router.get(‘/register’, (req, res) => {
res.render(‘register.ejs’, { err: “”});
});
router.get(‘/reset’, (req, res) => {
res.render(‘reset.ejs’, { err: ”});
});
router.get(‘/usercenter’, async (req, res) => {
const result = await model.findOne({_id: req.cookies.userid});
if (!result) {
res.redirect(‘/login’)
return
}
res.render(‘usercenter.ejs’, { err: “”});
});
module.exports = router;

四、post 模块,处理各种数据库的 CRUD 操作,后台逻辑。(核心)
1.CRUD 操作全部依赖模型对象来执行。
2. 限制对象一旦生成那么无法改变,除非删除数据库
3. 限制对象的增删改查都返回的是一个 promise 对象,
如果这时候去 if() 里判断,无论有什么样的结果,都是 true,
而且这个 CRUD 操作都是异步,所以我们把外部函数变成 async 函数,
这样可以配合 await 实现最佳异步,还可以获取他们的返回值进行
if 判断。(Node.js 的后端核心)

const {Router} = require(‘express’);
const express = require(‘express’);
const model = require(‘../common/model’);
const cookieParse = require(‘cookie-parser’);
const sha1 = require(‘sha1’);
const router = new Router();
router.use(cookieParse())
router.use(express.urlencoded({ extended: true}))
router.post(‘/login’, async (req, res) => {
const {username, password} = req.body;
const result = await model.findOne({username, password: sha1(password) });
if (!result) {
res.render(‘login’, { err: { usernameloginerr: ‘ 用户名或密码错 ’, username: username} })
return;
}
const userid = result.id;
res.cookie(‘userid’, userid, {maxAge:1000*60*10});
res.redirect(‘/usercenter’)
return
});
router.post(‘/register’, async (req, res) => {
const {username, password, repassword, email} = req.body;
const err = {};
const usernameReg = /^[A-Za-z0-9_]{5,10}$/;
const passwordReg = /^[A-Za-z0-9_]{5,12}$/;
const emailReg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
if (!usernameReg.test(username)) {
err.usernamereerr = ‘ 用户名格式错误 ’;
}
if (!passwordReg.test(password)) {
err.passworderr = ‘ 密码格式错误 ’;
}
if (repassword !== password) {
err.repassworderr = ‘ 两次密码输入不一致 ’;
}
if (!emailReg.test(email)) {
err.emailerr = ‘ 邮箱格式错误 ’;
}
const usernameresult = await model.findOne({username});
if (usernameresult) {
err.usernamereerr = ‘ 用户名已存在 ’;
res.render(‘register’, { err})
return
};
const emailresult = await model.findOne({email});
if (emailresult) {
err.emailerr = ‘ 邮箱已被注册 ’;
res.render(‘register’, { err})
return
}
if (err.usernamereerr || err.passworderr || err.repassworderr || err.emailerr) {
err.username = username;
err.email = email;
res.render(‘register’, { err})
return
}
model.create({
username: username,
password: sha1(password),
email: email
})
res.redirect(‘/index’)
});
router.post(‘/reset’, async (req, res) => {
const {username, password, repassword, email} = req.body;
const err = {};
const result = await model.findOne({username, email});
if (!result) {
if (repassword !== password) {
err.repassworderr = ‘ 两次密码输入不一致 ’
}
err.usernamereerr = ‘ 用户名或者邮箱输入有误 ’;
err.emailerr = ‘ 用户名或者邮箱输入有误 ’;
res.render(‘reset.ejs’, { err})
return
} else {
await model.updateOne({username, email}, {password: sha1(password) });
res.redirect(‘/usercenter’);
return
}

})
module.exports = router;

五、工具类模块 model 对象和 database 模块 有天坑需要注意
!!!!!!!!! 限制对象一旦生成那么无法改变,除非删除数据库
‘database 模块 ’

const mongoose = require(‘mongoose’);
module.exports = new Promise((resolve, reject) => {
mongoose.connect(‘mongodb://localhost:27017/userinfos’, { useCreateIndex: true, useNewUrlParser: true});
mongoose.connection.once(‘open’, err => {
if (!err) {
console.log(‘ 数据库连接成功 ’)
resolve()
} else {
console.log(‘ 数据库连接失败 ’, err)
reject(err)
}
})
})

——

‘model 对象模块 ’
‘ 这里定义限制对象时,一定要考虑好,
否则数据库连接启动后,除非删除数据库,
不然无法修改限制对象的内容!!!!’
const {Schema, model} = require(‘mongoose’);
const ajaxschema = new Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
})
const model1 = model(‘userinfo’, ajaxschema);
module.exports = model1;

六、ejs 的渲染目录
ejs 的渲染数据在 ejs 文件中的格式有三种
1. <% data %> 里面可以写任意代码
2. <%= data %> 里面写的代码最终会转义后再出现 (推荐)
3. <%- data %> 里面写的代码最终不会转义后就出现 (不安全)

‘index.ejs ‘

<!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>Document</title>
</head>
<body>
<h1> 欢迎来到首页 </h1>
<a href=”http://localhost:8080/login”> 登陆 </a>
<a href=”http://localhost:8080/register”> 注册 </a>
<a href=”http://localhost:8080/usercenter”> 个人中心 </a>
</body>
<script src=”https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js”></script>
<script>

</script>

</html>

——-

‘login.ejs’

<!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>Document</title>
</head>
<body>
<form method=”post” action=”/login”>
<label for=”username”> 用户名 </label>
<input type=”text” name=”username” value=<%= err.username %>><%= err.usernameloginerr %> <br />
<label for=”password”> 密码 </label>
<input type=”password” value=”” name=”password”>
<input type=”submit” id=”sub”>
</form>
<a href=’/reset’> 找回密码 </a>
</body>
<script>

</script>

</html>

————

‘register.ejs’

<!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>Document</title>
</head>

<body>
<form method=”post” action=”/register”>
<label for=”usern ame”> 用户名 </label>
<input type=”text”name=”username” value= <%= err.username %> > <%= err.usernamereerr %></br>
<label for=”password”> 密码 </label>
<input type=”password” value=”” name=”password”> <%= err.passworderr %></br>
<label for=”repassword”> 再次确认密码 </label>
<input type=”password” value=”” name=”repassword”> <%= err.repassworderr %></br>
<label for=”email”> 邮箱 </label>
<input type=”text” name=”email” value= <%= err.email %> > <%= err.emailerr %></br>
<input type=”submit”>
</form>
</body>

</html>

———–

‘reset.ejs’

<!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>Document</title>
</head>
<body>
<form method=”post” action=”/reset”>
<label for=”usern ame”> 您注册的用户名 </label>
<input type=”text” name=”username” value=<%= err.username %>> <%= err.usernamereerr %></br>
<label for=”password”> 重置后的密码 </label>
<input type=”password” value=”” name=”password”> <%= err.passworderr %></br>
<label for=”repassword”> 请再次确认密码 </label>
<input type=”password” value=”” name=”repassword”> <%= err.repassworderr %></br>
<label for=”email”> 您的注册邮箱 </label>
<input type=”text” name=”email” value=<%= err.email %>> <%= err.emailerr %></br>
<input type=”submit”>
</form>
</body>

</html>

——

‘usercenter.ejs’

<!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>Document</title>
</head>
<body>
<h1> 欢迎来到个人中心 </h1>
<a href=”/index”> 返回主页 </a>
</body>
</html>

——
七、后期会放一波 TypeScript 重构项目的源码,目前还需要调试。

正文完
 0