express
express:目前最流行的nodejs框架、
++windows系统的重命名原理是新建一个文件,然后将原来文件中的内容拷入新建文件夹,所以最好将node_modules删掉重装一下++
express的基本使用
安装:
npm install express --save
const express=require('express');
//创建服务
const app=express();
// 是get请求
// 路由第一个参数的规则
// 路径 是一个pathname
// 路径路径可以是字符串,字符串模式或正则表达式。
// 路径需要跟参数一完全匹配才可以进入路由的处理
// 那么就会执行第二个参数的回调,处理该请求
app.get('/', (request, response)=>{
response.write('hello world 你好');
response.end();
});
// 启动服务
// 参数1: 端口号
// 参数2: 域名
// 参数3: 最大的连接数量
// app.listen(8000, 'localhost', 512, ()=>{})
app.listen(8000,(error)=>{
if(error) throw error;
console.log('server is run: http://localhost:8000');
})
// 路由第一个参数的规则
// 路径如果有‘:’修饰,那么这是一个参数
app.get('/users/:userId/books/:bookId', (request, response)=>{
....
}
next 指针
app.get('/', (request, response, next)=>{
// request客户端的请求
// response将要响应课客户端的响应对象
// next指针
next();
});
路由处理的函可以传入多个或者以数组形式传入
app.get('/', func1);
app.get('/', func1, func2);
app.get('/', [
func1,
func2
], func3);
路由
路由是指应用程序端点(URI)的定义以及它们如何响应客户端请求。
URI包括url但不完全等于url
// GET method route
app.get('/', function (req, res) {
res.send('GET request to the homepage')
})
// POST method route
app.post('/', function (req, res) {
res.send('POST request to the homepage')
})
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...')
next() // pass control to the next handler
})
其他方法:
get
,post
,put
,head
,delete
,options
,trace
,copy
,lock
,mkcol
,move
,purge
,unlock
,report
,mkactivity
,checkout
,merge
,m-search
,notify
,subscribe
,unsubscribe
,patch
和search
。
路由转换为无效JavaScript变量名称的方法:
app['m-search']('/', function ...
路由路径
路径路径可以是字符串,字符串模式或正则表达式。
路线参数
Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "bookId": "8989" }
路由处理程序
路由处理程序可以采用函数,函数数组或两者的组合的形式。
next('route')
绕过剩余的路由回调。
response响应方法
方法 | 描述 |
---|---|
res.download() | 提示要下载的文件。 |
res.end() | 结束响应过程。 |
res.json() | 发送JSON响应。 |
res.jsonp() | 用JSONP支持发送JSON响应。 |
res.redirect() | 重定向请求。 |
res.render() | 呈现视图模板。 |
res.send() | 发送各种类型的响应。 |
res.sendFile() | 以八位字节流的形式发送文件。 |
res.sendStatus() | 设置响应状态代码并将其字符串表示形式作为响应主体发送。 |
res.status() | 设置响应状态代码 |
response响应对象
app.get('/',(req,res)=>{
let obj={a:1,b:2};
//响应json数据
res.json(obj);
//让客户端下载资源
res.download('../文件.zip');
//响应jsonp格式的数据
res.jsonp(obj);
// 重定向
res.redirect('/home');
// write end 合体
res.end(4);
// 响应的是文件
res.sendFile(__dirname + '/app.js');
//响应状态码,并且statusMessage做为响应的内容
res.sendStatus(404);
// 等价的
// res.statusCode = 200;
// res.status(200);
})
app.all()
get post put … 不分请求方法,但是路径需要完全匹配
执行函数就会调用
app.all('/',(req,res,next)=>{
....
})
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')
})
凡是由app可以调用的方法,router统统可以调用,router会自动判断res.url(get,post,detele,put…)
使用中间件
中间件功能是可以访问请求对象(req
),响应对象(res
)以及应用程序请求 - 响应周期中的下一个中间件功能的函数。下一个中间件函数通常用名为next
的变量表示。
中间件功能可以执行以下任务:
-
执行任何代码。
-
对请求和响应对象进行更改。
-
结束请求 - 响应循环。
-
调用堆栈中的下一个中间件功能。
如果当前的中间件功能没有结束请求 - 响应周期,则它必须调用next()
以将控制传递给下一个中间件功能。否则,请求将被挂起。
var express = require('express')
var app = express()
var myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
app.use(myLogger)
//只有'/api'开头的请求,才会进入到中间件中
// use的第一个参数,起到过滤的功能
app.get('/api', function (req, res) {
res.send('Hello World!')
});
/*
/api 可以匹配
/api/ 可以匹配
/apiabc 不可以匹配
/api/abc 可以匹配
/api/abc/abc.... 可以匹配
*/
app.listen(3000)
app.use('/api', (req, res, next)=>{
console.log('执行了1');
console.log(req.baseUrl); // /api
console.log(req.url); // /test
console.log(req.originalUrl); // /api/test
req.name = 'hello wrold';
next();
})
app.get('/api/test', (req, res)=>{
console.log('执行了2');
console.log(req.url);// /api/test
console.log(req.name); //hello world
res.end('ok');
})
可配置的中间件
//File: my-middleware.js
module.exports = function(options) {
return function(req, res, next) {
// Implement the middleware function based on the options object
next()
}
}
//使用中间件
var mw = require('./my-middleware.js')
app.use(mw({ option1: '1', option2: '2' }))
Express应用程序可以使用以下类型的中间件:
-
Application-level middleware(应用程序级别中间件)
-
路由器级中间件
-
错误处理中间件
-
内置中间件
-
第三方中间件
Application-level middleware
这个例子显示了没有安装路径的中间件功能。每次应用程序收到请求时都会执行该功能。
var app = express()
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
此示例显示/user/:id
路径上安装的中间件功能。该函数针对/user/:id
路径上的任何类型的HTTP请求执行。
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method)
next()
})// /user/007 /hello/123
这个例子显示了一个路由及其处理函数(中间件系统)。该函数处理对/user/:id
路径的GET请求。
app.get('/user/:id', function (req, res, next) {
res.send('USER')
})// /user/007 /hello/123
下面是一个在装载点加载一系列中间件功能的例子,带有装载路径。它演示了一个中间件子堆栈,用于打印任何类型的HTTP请求到/user/:id
路径的请求信息。
app.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
此示例显示处理GET请求到/user/:id
路径的中间件子堆栈。
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id)
next()
}, function (req, res, next) {
res.send('User Info')
})
// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
res.end(req.params.id)
})
自定义正向代理中间件
const map = {
http: require('http'),
https: require('https'),
}
module.exports = ({host, port, protocol = 'http'})=>{
return (req, res, next)=>{
map[protocol].request({
hostname: host,
port,
path: req.originalUrl,
method: 'GET'
}, (response)=>{
// 获得数据的content-type, content-length
res.setHeader('content-type', response.headers['content-type']);
res.setHeader('content-length', response.headers['content-length'] || '');
response.on('data', (bf)=>{
res.write(bf);
})
response.on('end', ()=>{
res.end();
})
}).end();
}
}
next(‘route’)
app.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next route
if (req.params.id === '0') next('route')
// otherwise pass the control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// render a regular page
res.render('regular')
}, function (req, res, next) {
// render a regular page
res.render('regular')
}, function (req, res, next) {
// render a regular page
res.render('regular')
})
// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
res.render('special')
})
路由器中间件
var app = express()
var router = express.Router()
// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}, function (req, res, next) {
console.log('Request Type:', req.method)
next()
})
// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next router
if (req.params.id === '0') next('route')
// otherwise pass control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// render a regular page
res.render('regular')
})
// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
console.log(req.params.id)
res.render('special')
})
// mount the router on the app
app.use('/', router)
app.use('/api/user', require('./routes/userRouter'));
app.use('/api/jobs', require('./routes/JobsRouter'));
app.use('/api/company', require('./routes/companyRouter'));
这样就可以在userRouter,JobsRouter,companyRouter对对应的api进行统一的处理
错误处理中间件
错误处理中间件始终需要四个参数。您必须提供四个参数来将其标识为错误处理中间件功能。即使您不需要使用该next
对象,也必须指定它以维护签名。否则,该next
对象将被解释为常规中间件,并且将无法处理错误。
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
内置中间件
Express具有以下内置中间件功能:
-
express.static提供静态资源,如HTML文件,图像等。
-
express.json使用JSON有效负载分析传入的请求。(解析字典类型的数据)注:适用于Express 4.16.0+
-
express.urlencoded使用URL编码的有效负载分析传入的请求。(解析字符串格式的数据)注:适用于Express 4.16.0+
-
body-parser老的项目用这个中间件来解析,现在已经不推介使用了
express.static()调用得到是中间件
处理静态资源文件的
当请求为/static/*时,按照url路径在'./static'路径下查找资源,响应给客户端。
app.use('/static',express.static('./static'))
直接用nodeJs获取客户端post请求参数:
app.post('/list/test',(req,res)=>{
console.log('接收到了客户端的post请求:/list/test');
// post请求参数?
let params = '';
req.on('data', (bf)=>{
params += bf;
})
req.on('end', (bf)=>{
console.log(params);
let paramsObj = require('querystring').parse(params);
console.log(paramsObj);
req.body = paramsObj;
})
使用express框架获取客户端post请求参数:
// 解析字符串格式的数据
app.use(express.urlencoded({extended: false}));//4.16新增的中间件
app.post('/list/test', (req, res)=>{
console.log(req.body);
});
第三方中间件
https://github.com/senchalabs/connect#middleware
常用第三方中间件:
- 跨域中间件: cors
- 日志记录: morgan
- 数据校验中间件:express-validator
// middleware/vaildator/userVaildator.js
const { body } = require('express-validator')
const validate = require('./errorBack')
const { User } = require('../../model/index')
module.exports.register = validate([
body('username')
.notEmpty().withMessage('用户名不能为空').bail()
.isLength({ min: 3 }).withMessage('用户名长度不能小于3').bail(),
body('email')
.notEmpty().withMessage('邮箱不能为空').bail()
.isEmail().withMessage('邮箱格式不正确').bail()
.custom(async val => {
const emailValidate = await User.findOne({ email: val })
if (emailValidate) {
return Promise.reject('邮箱已被注册')
}
}).bail(),
body('phone')
.notEmpty().withMessage('手机号不能为空').bail()
.custom(async val => {
const phoneValidate = await User.findOne({ phone: val })
if (phoneValidate) {
return Promise.reject('手机号已被注册')
}
}).bail(),
body('password')
.notEmpty().withMessage('密码不能为空').bail()
.isLength({ min: 5 }).withMessage('用户名长度不能小于5').bail(),
])
// middleware/vaildator/errorBack.js 用来统一处理错误然后返回的中间件
const { validationResult } = require('express-validator')
module.exports = validator => {
return async (req, res, next) => {
await Promise.all(validator.map(validate => validate.run(req)))
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(401).json({ error: errors.array() })
}
next()
}
}
// router/user.js
const express = require('express")
const router = express.Router()
const userController = require('../controller/userController')
const validator = require('../middleware/validator/userValidator')
router
.post ('/register', validator.register, usercontroller.register)
.get('/list', userController.list)
.delete('/' ‚usercontroller.delete)
module.exports = router
使用模板引擎
(dust|ejs|hbs|hjs|jade|pug|twig|vash)。默认 jade
0.app.set('views cache',false);
设置模板解析是否缓存,true
的话,只有第一个用户访问的时候需要解析模板,将解析过后的页面存入缓存,后面访问的用户就可以直接使用缓存,减少性能消耗
1.views
,模板文件所在的目录。例如:app.set('views', './views')
。这默认为views
应用程序根目录中的目录。
2.view engine
,要使用的模板引擎。例如,要使用ejs模板引擎:app.set('view engine', 'ejs')
。
3.app.engine(ext, callback)
(html模板引擎里面没有需要自己配置)
app.engine('html', require('ejs').renderFile);
在这种情况下,EJS提供了一个具有Express期望的相同签名(path, options, callback)
的.renderFile()
方法,但请注意,它在ejs.__express
内部将此方法进行了别名,因此如果使用“.ejs”扩展名,则不需要执行任何操作。
错误处理
您最后定义错误处理中间件,在其他app.use()
路由之后并路由呼叫; 例如:
var bodyParser = require('body-parser')
var methodOverride = require('method-override')
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(function (err, req, res, next) {
// logic
})
var bodyParser = require('body-parser')
var methodOverride = require('method-override')
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
//打印错误
app.use(logErrors)
//判断是否是ajax请求,如果是,响应500
app.use(clientErrorHandler)
//是页面请求,告诉你一个404的页面
app.use(errorHandler)
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
function errorHandler (err, req, res, next) {
res.status(500)
res.render('error', { error: err })
}
调试Express
Mac or linux:
$ DEBUG=express:* node index.js
windows:
set DEBUG=express:* & node index.js
补充
Request 对象
request对象表示HTTP请求,包含了请求查询字符串,参数,内容,HTTP头部等属性。常见属性有:
- req.app:当callback为外部文件时,用req.app访问express的实例
- req.baseUrl:获取路由当前安装的URL路径
- req.body / req.cookies:获得「请求主体」/ Cookies
- req.fresh / req.stale:判断请求是否还「新鲜」
- req.hostname / req.ip:获取主机名和IP地址
- req.originalUrl:获取原始请求URL
- req.params:获取路由的parameters
- req.path:获取请求路径
- req.protocol:获取协议类型
- req.query:获取URL的查询参数串
- req.route:获取当前匹配的路由
- req.subdomains:获取子域名
- req.accpets():检查请求的Accept头的请求类型
- req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages
- req.get():获取指定的HTTP请求头
- req.is():判断请求头Content-Type的MIME类型
Response 对象
response对象表示HTTP响应,即在接收到请求时向客户端发送的HTTP响应数据。常见属性有:
- res.app:同req.app一样
- res.append():追加指定HTTP头
- res.set()在res.append()后将重置之前设置的头
- res.cookie(name,value ,option):设置Cookie
- opition: domain / expires / httpOnly / maxAge / path / secure / signed
- res.clearCookie():清除Cookie
- res.download():传送指定路径的文件
- res.get():返回指定的HTTP头
- res.json():传送JSON响应
- res.jsonp():传送JSONP响应
- res.location():只设置响应的Location HTTP头,不设置状态码或者close response
- res.redirect():设置响应的Location HTTP头,并且设置状态码302
- res.send():传送HTTP响应
- res.sendFile(path ,options):传送指定路径的文件 -会自动根据文件extension设定Content-Type
- res.set():设置HTTP头,传入object可以一次设置多个头
- res.status():设置HTTP状态码
- res.type():设置Content-Type的MIME类型