Express
使用npm install express
命令安装express框架,以下简单是使用方式
const express = require("express");
const app = express(); //创建服务器对象
app.listen(8000); //该对象监听8000端口
console.log('Server running at http://127.0.0.1:8000/');
中间件机制
中间件就是处理客户端请求和响应的一串方法,也可以理解为拦截器,会按照中间件定义的顺序依次进行处理请求,中间件是以回调函数的形式的方法,该回调函数共有三个参数:
request
:客户端请求对象response
:服务器响应对象next
:默认情况下中间件依次向下匹配,一旦匹配成功就终止匹配,若再调用next()
方法就会接着向下匹配
请求响应中间件
省略path
参数时则表示全部请求都会拦截
app.use([path,]...callback)
:添加的中间件app.get([path,]...callback)
:get请求方法的中间件app.post([path,]...callback)
:post请求方法的中间件app.put([path,]...callback)
:put请求方法的中间件app.delete([path,]...callback)
:delete请求方法的中间件
请求对象扩展
属性或方法名 | 描述 |
---|---|
request.baseUrl |
路由路径,根路由为空字符串 |
request.query |
直接就是查询字符串的对象,无需手动进行转换 |
request.params |
路由参数对象,在请求路径中使用/:id 其中id就是一个路由参数,该参数存储在params 对象中 |
request.body |
post请求的请求体,无法直接使用,需要先安装body-parser 包,因为express.urlencoded() 和express.json() 都依赖于body-parser 包,再post请求之前添加该中间件即可,具体参考下面代码 |
/***********解析 request.body 模块************/
app.use(express.json()); //解析AJAX发送的json数据,即application/json
app.use(express.urlencoded({ extended: false })); //解析表单提交application/x-www-form-urlencoded
//urlencoded()该方法返回一个方法,所以可以当作中间件
//官方建议使用extended:false就是使用qierystring系统模块进行解析,否则会使用qs的第三方模块进行解析
响应对象扩展
方法名 | 描述 |
---|---|
response.send([body]) |
发送响应信息 当参数为字符串时,将返回html格式 当参数为数组或对象时,将返回JSON格式 |
response.sendFile(path) |
发送文件响应,但并非下载,path 是绝对路径 |
response.download(path[,filename,options,callback]) |
发送文件,即下载文件,path 可是相对路径,filename 是下载的文件名,callback 是错误回调 |
response.jsonp([body]) |
发送jsonp响应 |
response.status(code) |
设置响应状态码,返回response 对象,可链式调用 |
response.cookie(name,value[,options]) |
设置cookie |
response.clearCookie(name[,options]) |
删除cookie |
response.redirect([status,]path) |
重定向,可设置状态码301 或302 ,默认是302 |
错误处理中间件
比请求响应中间件多了一个参数,依然是错误优先的风格,4个参数依次是error
、request
、response
、next
只能使用app.use()
方法添加,应该放在中间件的末尾,当前其他中间件抛出错误时,会被该中间件拦截,error
参数就是抛出的错误对象
由于中间件是按照定义的顺序依次进行处理请求,所以无法捕获异步回调函数中的错误,需要手动触发,即在异步回调使用next(error)
方式,将错误对象传递到错误处理中间件(async函数使用同步try...catch方式捕获再调用next(errot)
方法即可)
路由中间件
使用express.Router()
方法可返回一个路由对象,路由对象其实就是一个方法,路由对象的作用就是为了才拆分不同路由,以实现模块化管理,基本使用如下:路由对象也相当于是一个服务器对象,所以甚至可以拆分多级路由
所有路由路径都要以/
开头
const express = require("express");
const app = express();
const homeRouter = express.Router(); //创建路由对象
homeRouter.get("/index.html", (request, response) => { //使用路由对象进行请求响应
response.send("访问的是 /home/index.html");
//访问http://127.0.0.1:8000/home/index.html即可得到响应
})
app.use("/home", homeRouter); //注册中间件路由对象
app.listen(8000);
console.log('Server running at http://127.0.0.1:8000/');
静态资源托管中间件
使用express.static(root,[option])
方法返回一个静态资源中间件方法,用来托管静态文件资源
const path = require("path");
app.use(express.static(path.join(__dirname, "public"), {
index: "index.html", //默认首页
maxAge: 24 * 60 * 60 * 1000 //将静态文件缓存浏览器的时间,单位毫秒
}));
其他常用中间件
cookies
在请求对象中添加的cookie信息(request.cookies
),需要先使用npm install cookie-parser
安装cookie-parser
包,再请求之前添加该中间件即可,具体参考下面代码
/***********解析 request.cookies 模块************/
const cookieParser = require("cookie-parser");
app.use(cookieParser());
session
在请求对象中添加的session信息(request.session
),需要先使用npm install express-session
安装express-session
包,再请求之前添加该中间件即可,具体参考下面代码
/***********解析 request.session 模块************/
const session = require("express-session");
app.use(session({
name: 'session.id', //cokkie中存储的sessionId名
secret: "secret", //密钥
resave: true, //是否请求重置cookie过期时效,默认true,不设置会报警告
saveUninitialized: true, //是否对于没有cookie的请求也设置上cookie,默认true,不设置会报警告
cookie: {
// path: '/', //session作用范围,默认就是 /
// httpOnly: true, //只允许服务端修改,默认就是true
maxAge: 30 * 1000 //session失效时间,单位毫秒
}
}))
/***********解析 request.session 模块存入redis中的配置************/
const session = require("express-session");
const RedisStore = require("connect-redis")(session); //若想将session存入redis,则需要下载connect-redis包
const sessionStore = new RedisStore({ //创建session仓库
clinent: require("./redisClinent") //redis的连接对象,redisClinent文件需要自己提供
});
app.use(session({
name: 'session.id', //cokkie中存储的sessionId名
secret: "secret", //密钥
resave: true, //是否请求重置cookie过期时效,默认true,不设置会报警告
saveUninitialized: true, //是否对于没有cookie的请求也设置上cookie,默认true,不设置会报警告
cookie: {
// path: '/', //session作用范围,默认就是 /
// httpOnly: true, //只允许服务端修改,默认就是true
maxAge: 30 * 1000 //session失效时间,单位毫秒
},
store: sessionStore //将session存入session仓库中
}))
日志记录
使用npm install morgan
安装该日志中间件
第一个参数为日志格式(可使用预设格式),第二个参数是配置项,其他日志格式和具体使用方式可从**官方**获得,一般开发环境和生产环境用默认格式和配置即可
开发环境配置
const morgan = require("morgan"); //引入morgan
app.use(morgan("dev")); //默认标准输出,开发环境中使用的开发格式
生产环境配置
const path = require("path");
const fs = require("fs");
const morgan = require("morgan"); //引入morgan
const logFile = path.join(__dirname, "log", "access.log"); //日志文件路径
const logStream = fs.createWriteStream(logFile, { flags: "a" }); //日志追加流
app.use(morgan("combined", { //日志格式为全模式
stream: logStream //向日志最佳流中写访问日志记录
}));
文件上传
使用npm install multer
安装该文件上传中间件
单文件上传
<form action="/upload" method="post" enctype="multipart/form-data">
<!-- 必须指定enctype为multipart/form-data -->
<input type="file" name="file">
<input type="submit" value="上传">
</form>
var multer = require("multer");
var upload = multer({ dest: "upload/" });
app.post("/upload", upload.single("file"), function (req, res, next) {
res.send(req.file);
});
多文件同name上传
<form action="/upload" method="post" enctype="multipart/form-data">
<!-- 必须指定enctype为multipart/form-data -->
<input type="file" name="file" multiple="multiple">
<!-- 多文件必须指定multiple属性才能多选 -->
<input type="submit" value="上传">
</form>
var multer = require("multer");
var upload = multer({ dest: "upload/" });
app.post("/upload", upload.array("file",2), function (req, res, next) {
//最多两个文件上传,若超过两个会抛出MulterError
//若不指定第二个参数则没有限制
res.send(req.files); //注意是files
});
多文件不同name上传
<form action="/upload" method="post" enctype="multipart/form-data">
<!-- 必须指定enctype为multipart/form-data -->
<input type="file" name="file1">
<input type="file" name="file2">
<input type="submit" value="上传">
</form>
var multer = require("multer");
var upload = multer({ dest: "upload/" });
app.post("/upload", upload.fields([{ //该方法接收一个对象数组
name: "file1", //file1最多上传两个文件
maxCount: 2
},
{
name: "file2" //file2文件
}
]), function (req, res, next) {
res.send(req.files); //注意是files
});
自定义文件名
var multer = require("multer");
var storage = multer.diskStorage({ //创建一个硬盘存储对象
destination: function (req, file, cb) { //存储目录
//req为上传文件的请求对象,file为上传的文件对象,cd为一个错误优先的回调函数,一般会传递错误对象,若错误对象为null则不会报错
//可根据这三个参数,可将复杂操作可以写成该插件的中间件
cb(null, "./upload"); //必须保证存储目录存在,否则会报错
},
filename: function (req, file, cb) { //存储文件名
//req为上传文件的请求对象,file为上传的文件对象,cd为一个错误优先的回调函数,一般会传递错误对象,若错误对象为null则不会报错
//可根据这三个参数,可将复杂操作可以写成该插件的中间件
cb(null, Date.now() + '-' + file.originalname); //文件名为时间戳加用户上传时的原文件名
}
})
var upload = multer({ storage: storage });
模板引擎
以art-template
模板引擎为例,该引擎支持胡子语法兼容ejs
模板引擎,该模板引擎的具体使用方法从官网可以获得,Express在该模板引擎上进一步进行了封装,所以需要安装express-art-template
和art-template
模板引擎
首先安装npm install express-art-template art-template
模板引擎,使用模板引擎的步骤如下:
app.engine("art", require("express-art-template")); //注册模板引擎
app.set("views", path.join(__dirname, "views")); //设置存储模板的文件夹
app.set("view engine", "art"); //设置模板的后缀
app.get("/", (request, response) => {
response.render("index", { msg: "message" }); //若有目录则需要指定目录dir/index
//使用index模板进行渲染,并指定数据对象
})
对于公共数据可以挂在app.locals
对象上,这样就可以在任意模板中拿到该数据,而不需要重复的在渲染方法中进行传递了,
app.locals.name="zhangsan";
<h1>{{name}}</h1>
Express生成器
一般脚手架工具都是全局安装,使用
-g
参数即可全局安装,使用时无需使用npx
命令前缀
首先使用npm install express-generator -D
命令本地安装脚手架工具,使用npx express 项目名
创建项目,默认模板引擎是jade,可使用view
参数指定其他支持的模板引擎,也可使用no-view
不适用模板引擎,其他参数适用help
参数进行查看,创建出的目录结构如下
文件或目录名 | 描述 |
---|---|
bin/www |
是项目的入口文件,仅提供http服务器对象用于启动项目 |
app.js |
各种中间件注册,比如:路由,静态资源,错误,404等 |
routes |
路由目录 |
views |
视图模板目录 |
public |
公共静态资源目录 |
app.js文件中其他中间件解释
var createError = require('http-errors'); //用于快速创建http错误响应的中间件
// catch 404 and forward to error handler
app.use(function(req, res, next) { //404错误处理中间件
next(createError(404)); //创建404http响应状态码,并将错误传给下面的错误处理中间件
});
app.use(function(err, req, res, next) { //错误处理中间件
// set locals, only providing error in development
res.locals.message = err.message; //将错误信息放入locals上,让所有模板都可以使用
res.locals.error = req.app.get('env') === 'development' ? err : {};
//开发模式下将错误对象locals上,非开发模式挂的是空对象,不会讲错误信息暴露给用户
// render the error page
res.status(err.status || 500); //设置http错误状态码
res.render('error'); //渲染视图模板中的error页面
});
www文件中其他中间件解释
var debug = require('debug')('项目名:server');
//debug模式下会输出信息 DEBUG=* 模式下启动
//有命名空间,比如 DEBUG=项目名:server 只会输出 项目名:server 的debug信息
debug('Listening on ' + bind);
//项目名:server Listening on 3000 +0ms
//以 项目名:server 为前缀,以距上次输出时间间隔为后缀
Comments NOTHING