原生nodejs中的res对象上的常用方法:
res.setHeader():设置响应的头信息,同一个路由中只能使用一次
res.writeHead():设置响应的头信息,同一个路由中只能使用一次
res.write():设置响应的内容,可以多次使用
res.end():结束响应,同一个路由中只能使用一次
express框架中的res对象上的常用方法:
res.send():设置响应的内容,相当于原生nodejs中的res.setHeader()和res.end()这两个方法,同一个路由中只能运行一次;
res.sendFile():将某个文件内容读取出来并作为响应返回给客户端,同一个路由中只能运行一次;
一、项目开发流程
把前端给的文件一一归类。
1、html页面放在views文件夹
2、静态资源放在static或public
3、配置一些post中间件(因为要接收post传递过来的参数)
4、配置开放静态资源的中间件
二、文件上传
2.1 前端
注意:当表单中含有文件上传的表单项时要把表单的编码格式设置为流媒体格式:enctype="multipart/form-data"
<form action="/zhuce" method="post" enctype="multipart/form-data">
<div>帐号:<input type="text" name="user"></div>
<div>密码:<input type="password" name="pwd"></div>
<div>密码:<input type="password" name="repwd"></div>
<div>头像:<input accept=".png,.gif,.jpg,.jpeg" type="file" name="pic"></div>
<div><input type="submit" value="注册"></div>
</form>
2.2 后端【重点】
在express框架中使用第三方formidable包来处理含有文件上传的表单:
app.post('/zhuce', (req, res) => {
let curPath = path.join(__dirname, 'uploads');
//判断文件或目录是否存在:
if (!fs.existsSync(curPath)) { //uploads目录不存在时则创建该目录
//创建目录:
fs.mkdirSync(curPath);
}
// multiples: true 支持多文件上传
//uploadDir:用来指定文件上传成功后的存放位置
const form = formidable({ multiples: true, uploadDir: curPath });
// console.log(form);
//form.parse():用来处理含有文件上传的表单
form.parse(req, (err, fields, files) => {
console.log(err, fields, files, 7777);
});
});
2.3 服务器处理完毕响应结果
保留原文件名:
生成及其他文件名:
1)年月日时分秒 + 8位随机数
2)使用第三方模块uuid,使用方法:uuid - npm。
app.post('/zhuce', (req, res) => {
let curPath = path.join(__dirname, 'uploads');
//判断文件或目录是否存在:
if (!fs.existsSync(curPath)) { //uploads目录不存在时则创建该目录
//创建目录:
fs.mkdirSync(curPath);
}
// multiples: true 支持多文件上传
//uploadDir:用来指定文件上传成功后的存放位置
const form = formidable({ multiples: true, uploadDir: curPath });
// console.log(form);
//form.parse():用来处理含有文件上传的表单
form.parse(req, (err, fields, files) => {
if (err) { //含有文件上传的表单处理失败
res.send('文件上传失败');
} else { //含有文件上传的表单处理成功
//要上传的原始文件名
let fileNames = files.pic.name;
let fArr = fileNames.split('.');
//获取文件扩展名:
let extName = fArr[fArr.length - 1]
// console.log(fArr, extName);
//将上传成功后的临时文件重命名成正式文件名:
fs.renameSync(files.pic.path, `${curPath}/${uuidv4()}.${extName}`);
res.send('Yes');
}
});
});
三、cookie
为什么要使用cookie、session?
因为http协议是无状态的协议,也就是服务器不会保存客户端的状态,当我们使用了cookie或session之后可以让我们的系统有记忆功能。
3.1 介绍
Cookie的产生也是HTTP的特点所决定的。HTTP协议有一个非常重要的特点是无状态的,也就是说当客户端请求服务器,每一个请求和响应结束以后,这次的连接是马上断开的(也是为了释放资源)。同时服务器是不保留连接者相关的信息。这就说明了,Cookie要解决HTTP无状态的问题。
3.2 cookie、session的特点(区别)【重点】
- cookie默认有效时间为整个有效会话期间、cookie可以设置有效时间,session默认有效时间为整个有效会话期间、session不能设置有效时间;
有效会话期间:是指当打开某个浏览器开始访问系统到关闭整个浏览器之前这段时间称为有效会话期间。 - cookie的大小限制在4KB左右、session没有限制;
- cookie存储在客户端、相对不安全,session存储在服务端,相对安全;
- cookie有数量限制:每个主机可以存放cookie的个数最多不能超过50个Cookie,session没有这方面的限制;
- 不同的浏览器之间不共享Cookie、session;
3.3 应用场景
3.4 cookie实现原理
3.5 cookie-parser
注意:获取cookie时要使用第三方cookie-parser中间件:
文档的参考地址: Express cookie-parser middleware - Express 中文文档 | Express 中文网
3.5.1 下载
npm install cookie-parser
3.5.2 引入并开启cookie功能
const cookieParser = require("cookie-parser"); // 引入cookieparser中间件
app.use(cookieParser()); // 使用cookie-parser中间件 全局开启cookie功用
3.5.3 设置cookie
res.cookie(key,value[,option])
参数说明:
option:可选参数,{ maxAge : ms} 过期时间,表示多长时间后过期。以毫秒为单位
文档的参考地址:Express 4.x - API Reference - Express 中文文档 | Express 中文网
3.5.4 获取cookie
req.cookies
3.5.5 案例
1)/login路由中通过req.cookie方法设置cookie
2)/welcome路由中设置欢迎界面
3)设计“/user/edit”路由,如果登录了可以正常访问,如果未登录则不让访问(权限管理雏形)
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');
//显示登录界面:
router.get('/login', (req, res) => {
res.sendFile(path.join(__dirname, 'login.html'));
});
router.get('/mycookie', (req, res) => {
res.send(`${JSON.stringify(req.cookies)}`);
});
//处理用户登录的表单:
router.post('/denglu', (req, res) => {
//接收post方式的键值对格式发送的参数:
console.log(req.body);
let { user = '', pwd = '' } = req.body;
if (user == '' || pwd == '') {
res.send(`<script>alert('帐号及密码都不能为空');location.href='/login';</script>`);
return;
}
//获取已注册的用户数据:
let uStr = fs.readFileSync(path.join(__dirname, 'user.json'));
let uObj = JSON.parse(uStr.toString());
if (user == uObj.user && pwd == uObj.password) {
//将登录成功的帐号保存到cookie中:
res.cookie('UNAME', user);
res.send(`<script>alert('登录成功');location.href="/welcome";</script>`);
// res.send(`<script>alert('登录成功');location.href="/welcome?uname=${user}";</script>`);
} else {
res.send(`<script>alert('登录失败:帐号或密码不正确');location.href='/login';</script>`);
}
});
//欢迎界面:
router.get('/welcome', (req, res) => {
// let uname = req.query.uname ? req.query.uname : '';
//获取cookie:获取当前已登录的帐号
let uname = req.cookies.UNAME ? req.cookies.UNAME : '';
res.send(`欢迎您${uname}回来`);
});
module.exports = router;
四、session
4.1 介绍
Session是另一种记录用户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。当服务端使用Session后,那么服务器会把Session信息存储在服务器上,同时Session标识也会记录在对应客户端上。
4.2 特点
- Session存储在服务器
- 依赖cookie
- 可以存储任意类型
- 通过客户端的sessionid标识符区分Session
- Session没有大小限制
4.3 应用场景
4.4. 实现原理
4.5 cookie-session
注意:不管是设置session还是获取session都要使用cookie-session这个中间件。
文档的参考地址: Express cookie-session middleware - Express 中文文档 | Express 中文网
4.5.1 下载
npm i cookie-session
4.5.2 引入并开启session功能
const cookieSession = require("cookie-session"); // 引入cookieSession中间件
// 使用cookieSession 中间件
app.use(cookieSession({
name: 'sessionids',//sessionid的名称,默认可以省略
keys: ['secret1','secret2','secret3'], //密钥锁
// Cookie Options
//maxAge: 24 * 60 * 60 * 1000 // 24 hours , session的有效期
}))
4.5.3 设置Session数据
req.session[key] = value
4.5.4 访问Session数据
req.session
案例代码如下:
const express = require('express');
var cookieSession = require('cookie-session')
const app = express();
app.listen(3000, () => {
console.log('3000端口');
});
//使用第三方cookie-session中间件来操作session:
app.use(cookieSession({
name: 'cursessions', //cookie名称
keys: ['dsafdsa^^%$', 'yes*&^%ok'], //密钥
}))
//设置session:
app.get('/setsession', (req, res) => {
req.session.TEL = 110;
req.session.EMAIL = { "email1": "lisi@qq.com", "email2": "demo@163.com" };
res.send(`<script>alert('设置session');location.href='/getsession'</script>`);
});
//获取session:
app.get('/getsession', (req, res) => {
let { TEL, EMAIL } = req.session;
console.log(TEL, EMAIL, 7779);
res.send(`获取session: ${JSON.stringify(req.session)}`)
});
五、ejs模板引擎
5.1 ejs模板引擎简介
为什么要使用ejs模板引擎?【重点】
使用ejs模板引擎可以让我们的界面与数据分离(比如:现在有1000条新闻,而这些新闻的展示界面风格都是一样的,这时我们只需要开发一个.html文件作为界面的模板文件,界面中的数据是动态获取到的), 可以提高开发效率,后期维护也方便。
5.2 使用步骤
1.下载ejs
npm i ejs
- 在项目中设置ejs模板引擎 【重点】
//设置ejs模板引擎:
app.set('view engine', 'ejs'); //设置模板引擎为ejs
//设置模板文件的存放位置
app.set('views', [path.join(__dirname, 'moban'), path.join(__dirname, 'views')]);
app.engine('html', require('ejs').__express); //将.html文件作为ejs模板文件来解析
3.在项目根目录下创建views文件夹
4.在views文件夹下创建模板文件
必须以 .ejs为后缀名
5.3 ejs语法
5.3.1 渲染语法
res.render('模板名称' [, data ])
参数说明:
参数1为模板名称不需要添加后缀,会自动寻找对应名称的.ejs文件
参数2为可选参数,向模板中传递的数据,必须是一个对象
5.3.2 模板语法
5.3.2.1 数据渲染
<%=变量名 %>
5.3.2.2 分支
<% if( 条件 ){%>
<% }else{ %>
<% } %>
5.3.2.3 循环
<% for(let i=0;i<数组.length;i++){ %>
循环项
<% }%>
5.3.2.4 实例
说明:包含文件也是.ejs文件,无需携带后缀名
<%- include('公共文件路径') %>
后端程序代码如下:
const express = require('express');
const path = require('path');
const app = express();
app.listen(3000, () => {
console.log('3000端口');
});
//设置ejs模板引擎:
app.set('view engine', 'ejs'); //设置模板引擎为ejs
//设置模板文件的存放位置
app.set('views', [path.join(__dirname, 'moban'), path.join(__dirname, 'views')]);
app.engine('html', require('ejs').__express); //将.html文件作为ejs模板文件来解析
app.get('/mydemo', (req, res) => {
//注意:ejs模板引擎的默认模板文件的扩展名为.ejs
res.render('demo'); //渲染[展示]模板
});
//新闻详情页面:
app.get('/detail', (req, res) => {
// req.query.nid; //新闻id
let ntitle = '这是新闻标题标题标题标题标题';
let ncontent = '这是文本内容这是文本内容这是文本内容这是文本内容这是文本内容';
let stu = [{ "uname": "李四", "age": 20, "email": "lisi@qq.com" },
{ "uname": "张三", "age": 21, "email": "zs@qq.com" },
{ "uname": "小明", "age": 16, "email": "ming@163.com" }];
res.render('news_detail.html', { ntitle, ncontent, stu }); //渲染[展示]模板
});
news_detail.html模板文件代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h3>新闻标题为:
<%=ntitle %>
</h3>
<div>新闻内容为:
<%=ncontent %>
</div>
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>邮箱</td>
</tr>
<% for(let i = 0;i<stu.length;i++){ %>
<tr>
<td>
<%=i+1 %>
</td>
<td>
<%=stu[i].uname %>
</td>
<td>
<%=stu[i].age %>
<%
if(stu[i].age>18){
%>
成年
<% }else{ %>
未成年
<% } %>
</td>
<td>
<%=stu[i].email %>
</td>
</tr>
<% } %>
<!-- <tr>
<td>2</td>
<td></td>
<td></td>
<td></td>
</tr> -->
</table>
</body>
</html>
5.3.2.5 包含文件
<%- include("header.html")%>