静态资源:
都写死了的资源,如 css,html
解析规则:
所有路由和中间件都在一个数组中,js 引擎会按照代码先后顺序添加路由和中间件
当请求发送到服务器时,服务器获取当前的请求信息(请求方式、请求路由路径)
遍历数组,找到第一个匹配(请求路由路径和请求方式必须完全一致)到的路由或者中间件,执行其回调函数
意味着: 声明多个同名路由时,始终解析第一个
如果没找到,返回一个状态码为 404 的响应, Cannot GET / xxx 或者 Cannot POST / xxx
中间件 middleware
当有多个路由做同一件事情,这时就会交给中间件去完成
本质上就是一个函数 (request, response, next)=>{}
express 框架 实现服务器 完全是由 路由 和 中间件 组成的
需要调用 next() 方法,才会接下来处理下面的中间件或者路由,否则卡住了
- app.use(express.static('./public')); //默认调用next
接受请求,通过分析参数,找到了 public 对应资源就返回响应
将该文件夹下所有静态资源暴露出去
例如: 文件夹有
public/index.html
public/css/index.css
就可在浏览器访问
127.0.0.1:3000/index.html
127.0.0.1:3000/css/index.css
- app.use(express.urlencoded({extended: true})); //默认调用next
解析 请求体 数据,结果数据挂载到 req.body 上
- 实例分析:
-
// 1. 导入 express 模块 const express = require('express'); // 2. 创建 app 应用对象 const app = express(); // 3. 写业务逻辑:处理请求,返回响应 /**** 配置内置 中间件 (express 自带) ****/ // 将该文件夹下所有静态资源暴露出去
// 接受请求,通过分析参数,找到了 public 对应资源就返回响应 app.use(express.static('./public')); // public 下有 index.html css/index.css // 可在127.0.0.1:3000/index.html css/index.css // 解析请求体数据,结果数据挂载到 req.body 上 app.use(express.urlencoded()); // 默认调用 next() /**** 以上两个一般结合使用 ****/
// 中间件默认能接收并处理所有请求
// 需要调用 next() 方法,才会接下来处理下面的中间件或者路由,否则卡住了
app.use((request, response, next)=>{
next(); // 调用下一个中间件或者路由
}); /**************************************/ // route 路由的组成: app.请求方式('/路由路径', 句柄函数); app.get('/login', (request, response)=>{ console.log(request.body); response.send('Login Page Response!'); }); app.post('/register', (request, response)=>{ console.log(request.query); response.send('Register Page Response!'); }); // 4. 监听端口号:一个端口号 有且只能运行 一个程序 app.listen(3000, err=>console.log(err?err:'服务器启动成功 : http://127.0.0.1:3000'));
登录/注册实例
- 什么时候用 get ,什么时候用 post?
get 请求 只有 请求字符串,
post 请求 既有 请求字符串,又有 表单数据 form-data
1. 凡是涉及到用户的隐私数据,就用 post,相对安全
2. 其他就用 get
- 业务逻辑
1. 导入 exoress 框架
2. 注册路由
由于默认访问不到静态资源,所以1引入中间件,暴露静态资源
默认不能解析 请求体 ,则2引入中间件,解析 请求体 数据(优先级低于上一中间件,避免多余分析)
① 获取用户提交的表单数据 req.body
② 对数据进行正则验证(验证数据的规范)
③ 检查用户名是否存在
连接数据库
User.findOne();
④ 保存在数据库中
User.create();
⑤ 无论 失败/成功 都要返回
3. 登录路由
4. 设置端口号,启动服务器监听
- 初次尝试,源代码
package.json
-
{ "name": "node_express", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "express": "^4.16.4", "mongoose": "^5.4.0" } }
/route/register.html
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>用户注册</title> <link rel="stylesheet" type="text/css" href="css/index.css"/> </head> <body> <div id="outer_box" class="register"> <h2>用户注册</h2> <form action="http://localhost:3000/register" method="post"> <div class="clothes"> <label for="input_name">用 户 名</label> <input id="input_name" type="text" name="user_name" placeholder="请输入用户名" /> </div> <div class="clothes"> <label for="input_pwd">密 码</label> <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" /> </div> <div class="clothes"> <label for="input_repwd">确认密码</label> <input id="input_repwd" type="password" name="user_repwd" placeholder="请再次输入密码" /> </div> <div class="clothes"> <label for="input_email">注册邮箱</label> <input id="input_email" type="text" name="user_email" placeholder="请输入邮箱地址" /> </div> <div class="clothes"> <button class="register btn" type="submit">注册</button> <a class="btn" href="http://localhost:3000/login"> <button type="button">登录</button> </a> </div> </form> </div> <script type="text/javascript" src="../index.js"></script> </body> </html>
/route/login.html
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>用户登录</title> <link rel="stylesheet" type="text/css" href="css/index.css"/> </head> <body> <div id="outer_box" class="login"> <h2>用户登录</h2> <form action="http://localhost:3000/login" method="post"> <div class="clothes"> <label for="input_name">用 户 名</label> <input id="input_name" type="text" name="user_name" placeholder="请输入用户名" /> </div> <div class="clothes"> <label for="input_pwd">密 码</label> <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" /> </div> <div class="clothes"> <a class="btn" href="http://localhost:3000/register"> <button type="button">注册</button> </a> <button class="login btn" type="submit">登录</button> </div> </form> </div> </body> </html>
/route/css/index.css
-
body { width: 100%; height: 100%; color: #000; background: #b9c2a4; background-size: cover; /* 指定背景图片大小 */ } /*************************************************/ #outer_box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #1a45c3; } #outer_box.login { color: #9e098b; } #outer_box.register { color: #1a45c3; } #outer_box>h2{ padding-bottom: 40px; margin-left: -50px; } .clothes { width: 260px; display: flex; justify-content: space-between; margin: 20px 0; font-size: 18px; line-height: 32px; } .clothes>label{ width: 80px; text-align: center; } .clothes>input{ width: 170px; height: 32px; } button { width: 100%; height: 100%; font-size: 16px; background-color: #c4ceda; cursor: pointer; } .clothes .btn{ width: 64px; height: 32px; margin: 0 20px; } .clothes button.register{ background-color: #1a45c3; color: #fff; } .clothes button.login{ background-color: #9e098b; color: #fff; }
/db/connectDB.js
-
const mongoose = require('mongoose'); const promiseConnect = new Promise((resolve, reject)=>{ mongoose.connect('mongodb://localhost:27017/user_database', {useNewUrlParser:true}) mongoose.connection.once('open', err=>{ if(err){ console.log(err); reject(err); }else{ resolve(true); }; }); }); module.exports = promiseConnect;
/db/tableModel.js
-
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const studentsSchema = new Schema({ "userName": { "type": String, "default": "test" }, "userPassword": { "type": String, "default": "123456" }, "userEmail": { "type": String, "unique": true }, "createTime": { "type": Date, "default": Date.now() } }); const studentsModel = mongoose.model("user_info", studentsSchema); module.exports = studentsModel;
index.js
-
const express = require('express'); const promiseConnect = require('./db/connectDB.js'); const userInfoModel = require('./db/tableModel.js'); promiseConnect.then(async result=>{ if(result){ isConeected = true; }; }).catch(err=>console.log(err)); const app = express(); /*********************** 中间件 **********************/ // 暴露路由 login.html register.html app.use(express.static('route')); // 默认调用 next(); // 将 用户输入的数据 挂载到 请求体 request.body 上 app.use(express.urlencoded({extended: true})); // 默认调用 next(); /************************ get ***********************/ app.get('/', (request, response)=>{ response.redirect('./login.html'); }); app.get('/login', (request, response)=>{ response.redirect('./login.html'); }); app.get('/register', (request, response)=>{ response.redirect('./register.html'); }); /************************ post ***********************/ let isConeected = false; let canBeCreated = false; let createSuccess = false; let logined = false; app.post('/register', async (request, response)=>{ if(isConeected){ let uName = request.body['user_name']; let uPwd = request.body['user_pwd']; let urePwd = request.body['user_repwd']; let uEmail = request.body['user_email']; userInfo = { "userName": uName, "userPassword": uPwd, "userEmail": uEmail }; canBeCreated = false; createSuccess = false; someThingWrong = false; console.log(' ------------------注册--------------------'); if(urePwd !== uPwd){ console.log("(づ╥﹏╥)づ[两次输入不一致`]"); response.redirect('./register.html'); return ; }else if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){ console.log("(づ╥﹏╥)づ[用户名不合法`]"); response.redirect('./register.html'); return ; }else if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){ console.log("(づ╥﹏╥)づ[密码不合法`]"); response.redirect('./register.html'); return ; }else if(!(/^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$/.test(uEmail))){ console.log('(づ╥﹏╥)づ[邮箱不合法`]'); response.redirect('./register.html'); return ; }else{ canBeCreated = true; const badEmail = await userInfoModel.findOne({"userEmail": uEmail}); if(badEmail){ console.log('(づ╥﹏╥)づ[邮箱已存在`]'); response.redirect('./register.html'); return ; }; }; const fond = await userInfoModel.findOne({"userName": uName}); if(!fond && canBeCreated){ await userInfoModel.create(userInfo); createSuccess = true; console.log('(o゜▽゜)o☆[注册成功!]☆'); }else{ console.log('(づ╥﹏╥)づ[用户名已存在`]'); }; }; if(createSuccess){ response.redirect('./login.html'); }else{ response.redirect('./register.html'); }; }); app.post('/login',async (request, response)=>{ if(isConeected){ let uName = request.body['user_name']; let uPwd = request.body['user_pwd']; userInfo = { "userName": uName, "userPassword": uPwd }; logined = false; console.log(' ------------------登录--------------------'); if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){ logined = false; // 用户名不存在 }else if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){ logined = false; // 密码错误 }; const findName = await userInfoModel.findOne({"userName": uName}); const findPwd = await userInfoModel.findOne({"userPassword": uPwd}); if(findName && findPwd){ logined = true; }; }; console.log(logined?'(o゜▽゜)o☆[登录成功!]☆':'(づ╥﹏╥)づ[用户名或密码错误`]'); response.redirect('./login.html'); }); /**************** 监听 3000, 启动服务器 ***************/ app.listen(3000, err=>console.log(err?err:' 服务器已启动: http://localhost:3000 Hunting Happy!'));
改进后,源代码
package.json
-
{ "name": "node_express", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "express": "^4.16.4", "mongoose": "^5.4.0" } }
templates/login.html
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>用户登录</title> <link rel="stylesheet" type="text/css" href="css/index.css"/> </head> <body> <div id="outer_box" class="login"> <h2>用户登录</h2> <form action="http://localhost:3000/login" method="post"> <div class="clothes"> <label for="input_name">用 户 名</label> <input id="input_name" type="text" name="user_name" placeholder="请输入用户名" /> </div> <div class="clothes"> <label for="input_pwd">密 码</label> <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" /> </div> <div class="clothes"> <a class="btn" href="http://localhost:3000/register"> <button type="button">注册</button> </a> <button class="login btn" type="submit">登录</button> </div> </form> </div> </body> </html>
templates/register.html
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>用户注册</title> <link rel="stylesheet" type="text/css" href="css/index.css"/> </head> <body> <div id="outer_box" class="register"> <h2>用户注册</h2> <form action="http://localhost:3000/register" method="post"> <div class="clothes"> <label for="input_name">用 户 名</label> <input id="input_name" type="text" name="user_name" placeholder="请输入用户名" /> </div> <div class="clothes"> <label for="input_pwd">密 码</label> <input id="input_pwd" type="password" name="user_pwd" placeholder="请输入密码" /> </div> <div class="clothes"> <label for="input_repeat_pwd">确认密码</label> <input id="input_repeat_pwd" type="password" name="user_repeat_pwd" placeholder="请再次输入密码" /> </div> <div class="clothes"> <label for="input_email">注册邮箱</label> <input id="input_email" type="text" name="user_email" placeholder="请输入邮箱地址" /> </div> <div class="clothes"> <button class="register btn" type="submit">注册</button> <a class="btn" href="http://localhost:3000/login"> <button type="button">登录</button> </a> </div> </form> </div> </body> </html>
templates/css/index.css
-
@charset "utf-8"; * { margin: 0px; padding: 0px; } .clearfix { zoom: 1; } .clearfix:before, .clearfix:after { content: ""; display: table; clear: both; } .unSelectedAble { /* 内容不可以被选中 */ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } body { width: 100%; height: 100%; color: #000; background: #b9c2a4; background-size: cover; /* 指定背景图片大小 */ } /*************************************************/ #outer_box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #1a45c3; } #outer_box.login { color: #9e098b; } #outer_box.register { color: #1a45c3; } #outer_box>h2{ padding-bottom: 40px; margin-left: -50px; } .clothes { width: 260px; display: flex; justify-content: space-between; margin: 20px 0; font-size: 18px; line-height: 32px; } .clothes>label{ width: 80px; text-align: center; } .clothes>input{ width: 170px; height: 32px; } button { width: 100%; height: 100%; font-size: 16px; background-color: #c4ceda; cursor: pointer; } .clothes .btn{ width: 64px; height: 32px; margin: 0 20px; } .clothes button.register{ background-color: #1a45c3; color: #fff; } .clothes button.login{ background-color: #9e098b; color: #fff; }
db/index.js
-
const mongoose = require('mongoose'); module.exports = new Promise((resolve, reject)=>{ mongoose.connect('mongodb://localhost:27017/user_database', {useNewUrlParser:true}) mongoose.connection.once('open', err=>{ if(err){ console.log(err); reject(err); }else{ resolve('数据库已连接'); }; }); });
models/index.js
-
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const fieldSchema = new Schema({ "userName": { "type": String, "unique": true, "required": true }, "userPassword": { "type": String, "unique": true, "required": true }, "userEmail": { "type": String, "unique": true, "required": true }, "createTime": { "type": Date, "default": Date.now() } }); module.exports = mongoose.model("user_info", fieldSchema);
index.js
-
const express = require('express'); const promiseConnect = require('./db'); const userInfoModel = require('./models'); const app = express(); /*********************** 中间件 **********************/ // 暴露路由 login.html register.html app.use(express.static('templates')); // 默认调用 next(); // 将 用户输入的数据 挂载到 请求体 request.body 上 app.use(express.urlencoded({extended: true})); // 默认调用 next(); /************************ get ***********************/ app.get('/', (request, response)=>{ response.redirect('./login.html'); }); app.get('/login', (request, response)=>{ response.redirect('./login.html'); }); app.get('/register', (request, response)=>{ response.redirect('./register.html'); }); /************************ post ***********************/ let logged = false ; promiseConnect.then(async result=>{ console.log(result); app.post('/register', async (request, response)=>{ const { user_name:uName, user_pwd:uPwd, user_repeat_pwd:urePwd, user_email:uEmail, } = request.body; /**** 解构赋值 ****/ // let uName = request.body['user_name']; // let uPwd = request.body['user_pwd']; // let urePwd = request.body['user_repeat_pwd']; // let uEmail = request.body['user_email']; userInfo = { "userName": uName, "userPassword": uPwd, "userEmail": uEmail }; let errInfo = {}; if(urePwd !== uPwd){ errInfo.repeatPassword = '两次输入不一致'; }; if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){ errInfo.name = '用户名不合法'; }; if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){ errInfo.password = '密码不合法'; }; if(!(/^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$/.test(uEmail))){ errInfo.email = '邮箱不合法'; }; const badEmail = await userInfoModel.findOne({"userEmail": uEmail}); if(badEmail){ errInfo.emailRegistered = '邮箱已被注册'; }; if(errInfo.repeatPassword || errInfo.name || errInfo.password || errInfo.email){ response.send(errInfo); return; }; const fond = await userInfoModel.findOne({"userName": uName}); if(fond){ response.send({"error":'用户名已被注册'}); }else{ await userInfoModel.create(userInfo); response.send({"success":'注册成功'}); }; }); app.post('/login',async (request, response)=>{ logged = false; let uName = request.body['user_name']; let uPwd = request.body['user_pwd']; userInfo = { "userName": uName, "userPassword": uPwd }; if(!(/^[a-zA-Z][a-zA-Z0-9_]{5,20}$/.test(uName))){ logged = false; // 用户名不存在 }else if(!(/^[a-zA-Z0-9_]{6,20}$/.test(uPwd))){ logged = false; // 密码错误 }; const findName = await userInfoModel.findOne({"userName": uName}); const findPwd = await userInfoModel.findOne({"userPassword": uPwd}); if(findName && findPwd){ logged = true; }; response.send(logged?{"success":'登录成功'}:{"error":'用户名或密码错误'}); }); }).catch(err=>console.log(err)); /**************** 端口号 3000, 启动服务器 ***************/ app.listen(3000, err=>console.log(err?err:' 服务器已启动: http://localhost:3000 Hunting Happy!'));