express
写在前面
有4.x 5.x 笔记内容为3.x 4.x新增改变可参考W3C
一些参考 :
express中文网
W3C
今年,你应该知道 23 个非常有用的 NodeJs 库
Node与Express开发之Express中间件
搭建
第一步:通过命令行创建一个空的文件夹
mkdir myapp
cd myapp
第二步:
npm init
//创建一个package.json文件 也是我们拿到其他人的文件包应该做的第一件事情
此命令行要求输入几个参数 可以一直按回车键设置为默认值,除了
entry point: (index.js)
第三步:如果你是用右键新建的文件夹 或者vscode
npm install express --save
//如果你希望安装一个临时的
npm install express
第一个程序:helloworld
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
以上代码启动一个服务并监听从3000端口进入的所有请求,只对(/)url或路由返回“hello world!”字符串。其他路径返回404 not found。
在命令面板输入,为了更方便的调试代码,建议设置热更新(当然还有其他)
node app.js
//设置热更新
npm install nodeamon
nodemon app.js
express应用生成
快速创建一个应用骨架
//安装
npm install express-generator -g
//查看
express -h
//在当前工作目录下创建一个名字为 myapp的应用
express myapp
#安装所有依赖包
cd myapp
npm install
//windo系统
set DEBUG=myapp & npm start
#浏览器打开 http://localhost:3000/ 网址
# 通过 Express 应用生成器创建的应用一般都有如下目录结构:
.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade
7 directories, 9 files
创建一个简单的express路由
首先,每一个路由都可以有一个或者多个处理器函数,当匹配到路由时,这个/些函数将被执行。
路由的定义由如下结构组成:app.METHOD(PATH, HANDLER)
。其中,app
是一个 express
实例;METHOD
是某个 HTTP 请求方式中的一个;PATH
是服务器端的路径;HANDLER
是当路由匹配到时需要执行的函数。
//req request res respone
// 对网站首页的访问返回 "Hello World!" 字样
app.get('/', function (req, res) {
res.send('Hello World!');
});
// 网站首页接受 POST 请求
app.post('/', function (req, res) {
res.send('Got a POST request');
});
// /user 节点接受 PUT 请求
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user');
});
// /user 节点接受 DELETE 请求
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user');
});
express托管静态文件
express.static
托管静态文件如 图片 css js文件等
app.use(express.static('public'))
静态文件夹 public 目录下的文件就可以访问了。如果有多个静态文件,可以使用多次 app.use(express.static('filename'))
访问静态资源文件时,express.static
中间件会根据目录添加的顺序查找所需的文件。
如果你希望所有通过 express.static
访问的文件都存放在一个“虚拟(virtual)”目录(即目录根本不存在)下面,可以通过为静态资源目录指定一个挂载路径的方式来实现,如下所示:
app.use('/static', express.static('public'));
然后就可以用过 /static 前缀来访问
express常见问题
express常见问题-W3Cschool
摘取几个比较关心的问题
如何处理404
在 Express 中,404 并不是一个错误(error)。因此,错误处理器中间件并不捕获 404。这是因为 404 只是意味着某些功能没有实现。也就是说,Express 执行了所有中间件、路由之后还是没有获取到任何输出。你所需要做的就是在其所有他中间件的后面添加一个处理 404 的中间件。如下:
app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});
如何设置一个错误处理器?
错误处理器中间件的定义和其他中间件一样,唯一的区别是 4 个而不是 3 个参数,即 (err, req, res, next)
:
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
请参考错误处理章节以了解更多信息。
用户信息验证
实际上我们会用上几个插件(中间件)来实现token的设置、密码加密、解密、以及输入格式的正则验证
给出一个开源的验证方法:
给出一段代码 app.js
(4.x)
// 导入 express 模块
const express = require('express')
//在路由之前导入
const joi = require('joi')
// 创建 express 的服务器实例
const app = express()
// 导入 cors 中间件 配置跨域
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())
// 设置跨域和相应数据格式
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header(
'Access-Control-Allow-Headers',
'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild'
)
res.header(
'Access-Control-Allow-Methods',
'PUT, POST, GET, DELETE, OPTIONS'
)
if (req.method == 'OPTIONS') {
res.cc('可以跨域', 0, true) /* 让options请求快速返回*/
} else {
next()
}
})
//配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据
app.use(express.json())
//为了验证码能通过,则需要导入session组件
const session = require('express-session')
// 配置session中间件
app.use(
session({
secret: 'keyboard cat', //服务端生成申明可随意写
resave: true, //强制保存session即使他没有什么变化
saveUninitialized: true, //强制将来初始化的session存储
})
)
// app.get('/put', (req, res) => {
// req.session.username = 'zhangshan'
// res.send('设置cookie成功!')
// })
// app.get('/get', (req, res) => {
// console.log(req.session.username)
// res.send('获取cookie成功!')
// })
// 定义send的响应数据的中间件
app.use(function (req, res, next) {
// status = 0 为成功; status = 1 为失败; 默认将 status 的值设置为 1,方便处理失败的情况
res.cc = function (err, status = 1, success = false) {
res.send({
success,
// 状态
status,
// 状态描述,判断 err 是 错误对象 还是 字符串
message: err instanceof Error ? err.message : err,
})
}
next()
})
// 导入配置文件
const config = require('./config')
// 解析 token 的中间件
const { expressjwt: expressJWT } = require('express-jwt')
// 使用 .unless({ path: [/^\/api\//] }) 指定哪些接口不需要进行 Token 的身份认证
app.use(
expressJWT({ secret: config.jwtSecretKey, algorithms: ['HS256'] }).unless({
path: [/^\/api\//],
})
)
// 导入并使用文章分类路由模块
const artCateRouter = require('./router/artcate')
// 为文章分类的路由挂载统一的访问前缀 /my/article
app.use('/my/article', artCateRouter)
// 导入并使用用户信息路由模块
const userinfoRouter = require('./router/userinfo')
// 注意:以 /my 开头的接口,都是有权限的接口,需要进行 Token 身份认证
app.use('/my', userinfoRouter)
// 导入并使用文章路由模块
const articleRouter = require('./router/article')
// 为文章的路由挂载统一的访问前缀 /my/article
app.use('/my/article', articleRouter)
// 托管静态资源文件
app.use('/uploads', express.static('./uploads'))
// 导入并注册用户路由模块
const userRouter = require('./router/user')
app.use('/api', userRouter)
// 错误中间件
app.use(function (err, req, res, next) {
// 数据验证失败
if (err instanceof joi.ValidationError) return res.cc(err)
// 捕获身份认证失败的错误
// console.log(err)
// console.log(req.headers.authorization)
if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
// 未知错误
res.cc(err)
})
// 调用 app.listen 方法,指定本地端口号8080 启动web服务器
let server = app.listen(8080, function () {
console.log('api server running at http://127.0.0.1:8080')
})
/**
*下面测试通信功能
*/
var io = require('socket.io').listen(server)
//socket连接
//socket连接
io.on('connection', (socket) => {
console.log('a user connected')
// socket.broadcast.emit('newPeer','new');
//将收到的消息广播出去
socket.on('sendMessage', function (content) {
console.log(content) //content为收到的消息内容,包括姓名和消息内容
socket.broadcast.emit('getMessage', content)
})
//监听新人连接,然后广播出去
socket.on('newPeople', function (name) {
console.log(name) //name为连接者的姓名或昵称
socket.broadcast.emit('newPeer', name)
})
})
//将vue项目打包成h5后,发起请求将html文件返回
app.get('/socket', (req, res) => {
res.sendfile('./index.html')
})
express路由
主要理解:如何设置 如何响应
路由方法
Express 定义了如下和 HTTP 请求对应的路由方法: get
, post
, put
, head
, delete
, options
, trace
, copy
, lock
, mkcol
, move
, purge
, propfind
, proppatch
, unlock
, report
, mkactivity
, checkout
, merge
, m-search
, notify
, subscribe
, unsubscribe
, patch
, search
, 和 connect
。
app.all()
是一个特殊的路由方法,没有任何 HTTP 方法与其对应,它的作用是对于一个路径上的所有请求加载中间件。任何的http请求句柄都会被执行
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...');
next(); // pass control to the next handler
});
路由路径
Express 使用 path-to-regexp 匹配路由路径,请参考文档查阅所有定义路由路径的方法。 Express Route Tester 是测试基本 Express 路径的好工具,但不支持模式匹配。
//字符串路由路径
app.get('/',function(req,res){
res.send("msg")
})
'/about' '/random.txt' 等
//字符串模式的路由路径
//匹配 acd 和 abcd
app.post('/ab?cd',function(req,res){
res.send('mag')
})
ab+cd // 匹配 abcd、abbcd、abbbcd等
ab*cd // 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
ab(cd)?e // 匹配 /abe 和 /abcde
//正则表达式的路由路径
//匹配任何含有 my 的路径
app.get(/my/,function(req,res){
res.send('/my/')
})
// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get(/.*fly$/, function(req, res) {
res.send('/.*fly$/');
});
路由句柄
为请求处理提供多个函数,其行为类似中间件
通过调用next('route')
方法可以略过其他路由的回调函数,利用该机制可以为路由定义前提条件,如果当前路径上继续执行是无意义的,可以将控制权交给剩下的路径
句柄可以是 函数、函数数组 或者两者混合
一个:
app.get('/example/a',function(req,res){
res.send('hello from a')
})
多个 (多个需要指定next())
app.get('/example/b',function(req,res){
console.log('respone will be sent by next function...');
next()
}),function(req,res){
res.send('hello from b')
}
函数数组
var cb0 = function (req, res, next) {
console.log('CB0');
next();
}
var cb1 = function (req, res, next) {
console.log('CB1');
next();
}
var cb2 = function (req, res) {
res.send('Hello from C!');
}
app.get('/example/c', [cb0, cb1, cb2]);
响应方法
方法 | 描述 |
---|---|
res.download() | 提示下载文件。 |
res.end() | 终结响应处理流程。 |
res.json() | 发送一个 JSON 格式的响应。 |
res.jsonp() | 发送一个支持 JSONP 的 JSON 格式的响应。 |
res.redirect() | 重定向请求。 |
res.render() | 渲染视图模板。 |
res.send() | 发送各种类型的响应。 |
res.sendFile | 以八位字节流的形式发送文件。 |
res.sendStatus() | 设置响应状态代码,并将其以字符串形式作为响应体的一部分发送。 |
app.route() & express.Router
app.route()创建路由路径的链式句柄
如:
app.route('/book')
.get(function(req, res) {
res.send('Get a random book');
})
.post(function(req, res) {
res.send('Add a book');
})
.put(function(req, res) {
res.send('Update the book');
});
express.Router
可使用 express.Router
类创建模块化、可挂载的路由句柄。Router
实例是一个完整的中间件和路由系统,因此常称其为一个 “mini-app”。
下面的实例程序创建了一个路由模块,并加载了一个中间件,定义了一些路由,并且将它们挂载至应用的路径上。
在 app 目录下创建名为 birds.js
的文件,内容如下:
var express = require('express');
var router = express.Router();
// 该路由使用的中间件
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// 定义网站主页的路由
router.get('/', function(req, res) {
res.send('Birds home page');
});
// 定义 about 页面的路由
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router;
然后在应用中加载路由模块:
var birds = require('./birds');
...
app.use('/birds', birds);
应用即可处理发自 /birds
和 /birds/about
的请求,并且调用为该路由指定的 timeLog
中间件。
express中间件
中间件是什么?
中间件是一个函数,可以访问请求对象(req),响应对象(res)和web应用中处于 请求-响应 循环流程中的中间件,一般被命名为next
的变量。
中间件的功能包括:
- 执行任何代码。
- 修改请求和响应对象。
- 终结请求-响应循环。
- 调用堆栈中的下一个中间件。
如果当前中间件没有终结请求-响应循环,则必须调用
next()
方法将控制权交给下一个中间件,否则请求就会挂起。
应用程序级中间件
通过app.use()
和app.method()
函数将应用程序级别的中间件绑定到app对象的实例,其中方法是中间件函数以小写形式处理的请求(get put post等)http方法。
路由器级中间件
路由器级中间件的工作方式与应用程序级中间件相同,只不过绑定到Express.Router()的实例上
var router=express.Router()
使用 router.use ()
和 router.Method ()
函数加载路由器级别的中间件。
错误处理中间件
错误处理中间件总是有四个参数。您必须提供四个参数来将其标识为一个错误处理中间件函数。即使不需要使用下一个对象,也必须指定它来维护签名。否则,下一个对象将被解释为常规中间件,并且将无法处理错误。
err,req,res,next
app.use(function(err,req,res,res,next){
console.erro(err.stack)
res.status(500).send('something broken')
})
在app.use()
和路由后调用,最后定义错误处理中间件
中间件返回的响应是随意的,可以响应一个 HTML 错误页面、一句简单的话、一个 JSON 字符串,或者其他任何您想要的东西。
为了便于组织(更高级的框架),您可能会像定义常规中间件一样,定义多个错误处理中间件。比如您想为使用 XHR 的请求定义一个,还想为没有使用的定义一个,那么:
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(bodyParser());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
logErrors
将请求和错误信息写入标准错误输出、日志或类似服务:
function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}
clientErrorHandler
的定义如下(注意这里将错误直接传给了 next
):
function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something blew up!' });
} else {
next(err);
}
}
errorHandler
能捕获所有错误,其定义如下:
function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}
next()传入的参数:
---- route: 跳转到下一个句柄
---- 其他:Express认为当前的请求有错误的输出,跳过后续其他非错误处理和路由/中间件函数;如果你需要做一些特殊处理,next传入的参数为 route
app.get('/a_route_behind_paywall',
function checkIfPaidSubscriber(req, res, next) {
if(!req.user.hasPaid) {
// 继续处理该请求
next('route');
}
}, function getPaidContent(req, res, next) {
PaidContent.find(function(err, doc) {
if(err) return next(err);
res.json(doc);
});
});
/*在这个例子中,句柄 getPaidContent 会被跳过,但 app 中为 /a_route_behind_paywall 定义的其他句柄则会继续执行。
next() 和 next(err) 类似于 Promise.resolve() 和 Promise.reject()。它们让您可以向 Express 发信号,告诉它当前句柄执行结束并且处于什么状态。next(err) 会跳过后续句柄,除了那些用来处理错误的句柄。*/
如果你向next()传递了一个error,但没有写错误处理函数处理这个error,Express 内置的缺省错误处理句柄就是最后兜底的。最后错误将被连同堆栈追踪信息一同反馈到客户端。堆栈追踪信息并不会在生产环境中反馈到客户端。
function errorHandler(err, req, res, next) {
if (res.headersSent) {//已经向respone发送数据了
return next(err); //才调用next()处理错误
}
res.status(500);
res.render('error', { error: err });
}
/*
,当你添加了一个自定义的错误处理句柄后,如果已经向客户端发送包头信息了,你还可以将错误处理交给 Express 内置的错误处理机制。
*/
内置中间件
like:
express.static(root,[option]) 静态资源托管
第三方中间件
例如 cookie的解析中间件
npm install cookie-parser
调试
[调试 Express](Express 调试_w3cschool)
设置代理
我不明白简哥