01-Express

nobility 发布于 2020-07-03 1963 次阅读


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) 重定向,可设置状态码301302,默认是302

错误处理中间件

比请求响应中间件多了一个参数,依然是错误优先的风格,4个参数依次是errorrequestresponsenext

只能使用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-templateart-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 为前缀,以距上次输出时间间隔为后缀
此作者没有提供个人介绍
最后更新于 2020-07-03