express简介
express是一个基于Node.js平台的极简的、灵活的WEB应用开发框架。express是一个封装好的工具包,封装了很多功能,便于我们开发WEB应用(HTTP服务)
express使用
新建express文件夹新建文件test01.js,代码如下
// 导入express
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由
app.get('./home', (req, res) => {
res.end('hello');
});
// 监听端口,启动服务
app.listen(3000, () => {
console.log('服务已经启动,端口3000监听中')
})
npm初始化,然后新建express-learn,接着运行node test01.js,报错
在目录下安装express并将其保存到依赖列表
npm install express --save
运行成功。
只有请求方法是get请求路径是/home的时候回调函数才会实现,请求的url路径是/,所以响应404
express路由
路由确定了应用程序如何响应客户端对特定端点的请求
express路由的使用
一个路由由请求方法,路径和回调函数组成
app.<method>(path.callback)
GET请求
app.get('/', (req, res) => {
res.end('home');
});
POST请求
app.post('/login', (req, res) => {
res.end('i m login')
})
使用表单发送post请求
<form method="post" action="http://127.0.0.1:3000">
<button>登录</button>
</form>
其他的一些具体案例
//post
app.post('/login', (req, res) => {
res.end('i m login')
});
// 匹配所有的方法
app.all('/test', (req, res) => {
res.end('test')
})
// 404响应
app.all('*', (req, res) => {
res.end('404 not found')
})
注意路径名不要添加点,不然可能会出现错误
获取报文数据
express框架封装了一些API来方便请求报文中的数据,并且兼容原生HTTP模块的获取方式。
核心代码
// 获取查询字符串
console.log(req.query);
// 获取指定的请求头
console.log(req.get('host'));
res.send('请求报文的获取');
获取路由参数
商城中的商品的id不同,对应的商品详情页面也不相同,所以使用下面的代码匹配
app.get('/:id.html', (req, res) => {
// 获取URL路由参数
console.log(req.params.id);
res.getHeader('content-type', 'text/html;charset=utf-8');
res.end('商品详情');
});
两个id名称保持一致。
express响应设置
express框架封装了一些API方便客户端响应数据,并且兼容原生HTTP模块的获取方式。
原生方式
app.get("/response", (req, res) => {
// express中设置响应的方式兼容HTTP模块的方式
res.statusCode = 404;
res.statusMessage = 'xxx';
res.setHeader('abc', 'xyz');
res.write('响应体');
res.end('xxx');
})
express的响应方法
res.status(500);
res.set('xxx', 'yyy');
res.send('中文响应不乱码');
// 连贯操作
res.status(404).set('xxx', 'yyy').send('你好朋友')
// 3其他响应
res.redirect('http://xxx.com')//重定向
res.download('./package.json');//下载响应
res.json();//响应json
res.sendFile(__dirname + '/home.html')//响应文件内容
json响应可以是下面这样
res.json({
name: '隐藏用户',
rank: 'top1'
});
将html文件内容响应给网页可以使用
res.sendFile(_dirname+'/test.html')
或者是path.resolve()
中间件
Middleware本质是一个回调函数,中间件函数可以像路由回调一样访问请求对象(request),响应对象response
中间件的作用
使用函数封装公共操作,简化代码
中间件的类型
- 全局中间件
- 路由中间件
定义全局中间件
每一个请求到达服务器之后都会执行全局中间件函数
执行下面的函数
app.get('/home', (req, res) => {
// 获取url和ip
let { url, ip } = req;
// 将信息保存在文件中access.log
fs.appendFileSync(path.resolve(__dirname, './access.log'), `${url} ${ip}\r\n`);///home 127.0.0.1
res.send('前台首页');
});
推荐插件Template String Converter,可以在${}输入字符串时候自动生成反引号
每个路由规则都要写app.get里面的两行代码,先上车后检查的类似操作后续维护不便,这是我们可以考虑中间件操作
function recordMiddleware(req, res, next) {
// 获取url和ip
let { url, ip } = req;
// 将信息保存在文件中access.log
fs.appendFileSync(path.resolve(__dirname, './access.log'), `${url} ${ip}\r\n`);///home 127.0.0.1
// 调用next
next();
}
// 使用中间件函数
app.use(recordMiddleware);
req是接收请求报文的对象,res是接收响应报文的对象,next是内部函数,执行后指向路由回调或者中间件回调.
路由中间件实践
需求:针对/admin /setting的请求,要求URL携带code=521的参数,如未携带提示【暗号错误】;
声明中间件函数,并设置在受约束的路由规则当中
// 声明中间件
let checkCodeMiddleware = (req, res, next) => {
// 判断URL中的是否code参数等于521
if (req.query.code === '521') {
// res.send('登录页面');不能每个请求都响应登录页面,这样其他页面都失效了
next();//满足条件就可以执行后续代码
} else {
res.send('暗号错误');
}
}
app.get('/admin', checkCodeMiddleware, (req, res) => {
res.send('后台首页');
});
// 后台设置
app.get('/setting', checkCodeMiddleware, (req, res) => {
res.send('设置页面');
});
先执行中间件里面的代码,执行next,执行路由回调。路由中间件就可以封装代码。项目中一般使用中间件校验用户身份或者权限。
静态资源中间件
静态资源中间件的设置
// 静态资源中间件设置
app.use(express.static(__dirname + '/public'));
express.static()返回结果是一个中间件函数,参数是静态资源文件夹的路径,服务端到文件夹寻找对应文件,读取文件,响应内容。
注意事项
-
express.html文件为默认打开的资源
-
如果静态资源和路由规则同时匹配,谁先匹配谁就响应
app.use(express.static(__dirname + '/public'));
app.get('/', (req, res) => {
res.send('前台首页');
});
两个同时匹配上,先匹配前面的就先响应前面的
3. 路由响应动态资源,静态资源中间件响应静态资源
获取请求体数据
核心步骤
- 安装
- 导入包
- 获取中间件函数
- 设置路由中间件,然后使用request.body来获取请求体数据
需求
按照要求搭建HTTP服务
get /login 显示表单网页
post /login 获取表单中的用户名和密码
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.get('/login', (req, res) => {
res.send('表单页面')
});
app.post('/login', (req, res) => {
res.send('获取用户的数据')
});
app.listen(3000, () => {
console.log('服务已启动')
})
post请求需要用表单来发起,新建form表单,修改路由规则里面响应html内容
app.get('/login', (req, res) => {
// res.send('表单页面')
res.sendFile(__dirname + '/_form.html')
});
因为响应的表单数据是querystring格式的,所以使用第二个
//解析json格式的请求体的中间件
var jsonParser = bodyParser.json()
// 解析querystrinng格式请求体的中间件
var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.post('/login', urlencodedParser, (req, res) => {
console.log(req.body);
res.send('获取用户的数据');
});
防盗链
在网页中选择图片,右键复制图片链接,将其引入html的img里面,打开html,有的显示有的不显示,不显示的就是开启了防盗链。防止外部网站盗用本网站资源。
设置一张图片可以127.0.0.1访问禁止localhost访问
// 声明中间件
app.use((req, res, next) => {
// 检测请求头中的referer是否为127.0.0.1
// 获取referer
let referer = req.get('referer');
if (referer) {//有refer进入函数内,没有直接下一步
// 实例化
let url = new URL(referer);
// 获取hostname
let hostname = url.hostname;
console.log(hostname)
if (hostname !== '127.0.0.1') {
res.status(404).send('<h1>404</h1>')
}
}
console.log(referer);
next();
})
app.use(express.static(__dirname + '/public'));
路由模块化
将路由的代码进行模块化处理,将13文件中的前台的路由移动到homeRouter.js中,然后在13文件中导入homeRouter,设置app.use(homeRouter)
主要步骤
- 导入express
- 创建路由对象
- 创建路由规则
- 暴露router
在文件13中引入
const homeRouter = require('./homeRouter')
app.use(homeRouter);
homeRouter的代码
const express = require('express');
const router = express.Router();
router.get('/home', (req, res) => {
res.send('前台首页');
});
router.get('/search', (req, res) => {
res.send('内容搜索');
});
module.exports = router;
后台也可以路由模块化处理。
EJS模板引擎
模板引擎是分离用户界面和业务数据的一种技术。
EJS是一个高效的JavaScript的模板引擎。
使用方法
下载安装
npm i ejs --save
使用步骤
- 下载安装
- 引入ejs
- 定义数据
- ejs解析模板返回结构
ejs列表渲染
const ejs = require('ejs');
let person = ['张三', '李四', '二狗'];
// js
let str = '<ul>';
person.forEach(item => {
str += `<li>${item}</li>`;
})
str += '</ul>';
console.log(str);
// ejs
let result = ejs.render(`<ul>
<% person.forEach(item =>{ %>
<li><%= item %></li>
<% }) %>
</ul>`, { person: person });
console.log(result)
将
<ul>
<% person.forEach(item=>{ %>
<li>
<%= item %>
</li>
<% }) %>
</ul>
写入html文件,然后编写EJS
const fs = require('fs');
let html = fs.readFileSync('./02ejs.html').toString();
let result = ejs.render(html, { person: person });
console.log(result)
EJS条件渲染
需求:通过isLogin决定最终的输出内容
true 输出 欢迎回来
false输出登录 注册
// 变量
let isLogin = true;
// 原生js
if (isLogin) {
console.log('<span>欢迎回来</span>')
} else {
console.log('<button>登录</button> <button>注册</button>')
}
// ejs
let result = ejs.render(`
<% if(isLogin){%>
<span>欢迎回来</span>
<% }else{%>
<button>登录</button> <button>注册</button>
<% }%>`,
{ isLogin: isLogin }
);
左边的isLogin和if里面的保持一致,,右边的和变量名称保持一致.
进一步操作,将ejs代码剪切到html中
<% if(isLogin){%>
<span>欢迎回来</span>
<% }else{%>
<button>登录</button> <button>注册</button>
<% }%>
在js文件中读取html文件
let html=fs.readFileSync('./_home.html').toString();
运行报错,原因忘记引入fs模块和ejs模块,引入后运行截图
express中使用ejs
模板文件:具有模板语法的文件,比如之前具有模板语法的html文件
res.render(‘模板的文件名’,‘数据’)
// 导入express
const express = require('express');
// 创建应用对象
const app = express();
//1. 设置模板引擎
app.set('view engine', 'ejs')
// 2.设置模板文件存放位置
app.set('views', path.resolve(__dirname, './views'));
// 创建路由
app.get('/home', (req, res) => {
// 3.render响应
//声明变量
let title = '隐藏用户top1';
res.render('home', { title });
// 4.模板文件
});
// 监听端口启动服务
app.listen(3000, () => {
console.log('服务启动,端口监听中')
})
express-generator
通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。
安装使用
npm install -g express-generator
安装完成后使用express -h可以查看帮助
将代码下载到一个新的文件夹下面
express -e 文件夹名称
新建的文件夹为generator,在generator里面安装依赖npm i
之后可以使用npm start运行项目
404的两种写法
app.use(function(req, res, next) {
next(createError(404));
});
app.all('*', (req, res) => {
res.end('404 not found')
})
查看文件上传报文
multipart/form-data是文件上传的必需属性。
npm start运行之后报错,
原因在于index.js里面没有写post
机械的敲代码,看走眼了。。。
修改之后页面功能全部正常
处理文件上传
首先安装一个包formidable
npm i formidable
引入formidable然后写入下面的代码
router.post('/portrait', (req, res) => {
// 创建form对象
const form = formidable({
multiples: true,
// 设置上传文件的保存目录
uploadDir: __dirname + '/../public/images',
// 保存文件后缀
keepExtensions: true
});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
// console.log(fields);
// console.log(files);
// 服务器保存该图片的访问url
let url = '/images/' + files.portrait.newFilename;//将来将此数据保存在数据库中
res.send('ok')
// res.json({ fields, files });
});
', (req, res) => {
// 创建form对象
const form = formidable({
multiples: true,
// 设置上传文件的保存目录
uploadDir: __dirname + '/../public/images',
// 保存文件后缀
keepExtensions: true
});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
// console.log(fields);
// console.log(files);
// 服务器保存该图片的访问url
let url = '/images/' + files.portrait.newFilename;//将来将此数据保存在数据库中
res.send('ok')
// res.json({ fields, files });
});