Node express笔记

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的设置、密码加密、解密、以及输入格式的正则验证

给出一个开源的验证方法:

github链接

给出一段代码 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)

设置代理

我不明白简哥
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值