express框架介绍与安装
express框架其实是nodejs内置http模块的扩展,主要用来:
1.快速搭建nodejs项目:这点功能有点类似于java里面的maven,使用express建立好的项目结构有固定的目录名称,每个目录都约定俗成的 存放着应该存放的文件,比如public目录一般用来存放资源文件
2.完成路由功能:在没有使用express之前,如果我们想要实现路径的路由,需要使用http模块获取url,加上n多个if else判断来实现,在 express里面实现起来相当简单方便、易于阅读
3.express的中间件:中间件(middleware)说白了就是处理HTTP请求的函数,用来完成各种特定的任务,比如检查用户是否登录、处理数据、以及其他在需要最终将数据发送给用户之前完成的任务。它最大的特点就是,一个中间件处理完,可以决定是否再传递给下一个中间件。
全局安装:
npm install -g express
本地安装:
npm install express --save-dev
package.json:
express快速上手
在安装好了express之后,我们来编写一个程序快速入门,新建一个a.js,编辑内容如下:
var express = require('express');
var app = express();
app.use(function(req,res){
console.log("第一个express快速入门");
});
app.listen(8080);
使用node运行a.js:
D:\zhao\nodews>node a
运行之后,程序阻塞在这里,此时在浏览器输入:http://localhost:8080 或者http://localhost:8080/abc或者http://localhost:8080/abc/123abc等待,都会输出第一个express快速入门这句话:
至此,express快速入门就完成了。
express的use方法
前边express的快速入门章节我们使用了express的use方法:
app.use(function(req,res){
console.log("第一个express快速入门");
});
其格式是:app.use([path,] callback [, callback...])
解释:当path不写的时候,所有请求都会被拦截,写上path则表示拦截path指定的路径。
回调函数的参数req、res分别表示请求对象和相应对象,除了这两个参数,还有第三个参数next,这个next是个函数,如果调用了这个函数,则表示继续执行下一个next。
使用use拦截指定路径,编写a.js:
var express = require('express');
var app = express();
app.use("/login",function(req,res){
console.log("登录");
});
app.use(function(req,res){
console.log("第一个express快速入门");
});
app.listen(8080);
使用命令node a运行,输入http://localhost:8080/login访问,控制台打印如下信息:
但是修改上边的代码,把两个use交换下位置:
app.use(function(req,res){
console.log("第一个express快速入门");
});
app.use("/login",function(req,res){
console.log("登录");
});
再访问http://localhost:8080/login的时候,将只打印“第一个express快速入门”这句话,这是因为,你并没有调用use回调函数的第三个参数next,next决定着是否继续按顺序往下执行下一个中间件(express里面的中间件就是一个函数)。我们修改a.js代码:
var express = require('express');
var app = express();
app.use(function(req,res,next){
console.log("第一个express快速入门"+req.path);
next();
});
app.use("/login",function(req,res){
console.log("登录");
});
app.listen(8080);
再访问http://localhost:8080/login结果如下:
express的app.get与app.post方法
前边说了express的app.use方法拦截指定的请求地址,但是我们还知道http请求分为get请求、post请求、update请求等等,都用use拦截显得有点不直观,所以express又给use方法取了很多别名,分别对应着http的get请求、post请求、update请求等,这就是express里面的app.get和app.post方法。也就是说,对于请求的拦截处理,你可以使用use也可以使用get或者post来分担use的功能。
app.get(path, callback [, callback ...]):用于拦截get请求
app.post(path, callback [, callback ...]):用于拦截post请求
我这里就以app.get方法为例吧,编写a.js:
var express = require('express');
var app = express();
app.get("/login",function(req,res){
console.log("登录");
});
app.get("/listUsers",function(req,res){
console.log("获取用户列表");
});
app.listen(8080);
运行node a,分别访问对应地址,结果如下:
D:\zhao\nodews>node a
登录
获取用户列表
nodejs使用express向浏览器发送数据或者页面
前面我们编写的基于express的程序比较简单,都是在中间件里面直接使用console.log打印一句话就了事了,这样子只能在控制台调试一下运行结果,如果我想向浏览器发送数据或者html页面该怎么做呢?我们使用nodejs的http模块的时候,在顶级路由判断的模块,假如要返回一个登陆页面login.html,我们就需要使用nodejs的fs模块把login.html读取近来并写入浏览器,在express里面的原理自然也是一样的,只是给我们提供的方法不同罢了。这里面express主要给我们提供了两个很常用的函数,
res.send([body]):用于向浏览器写入数据,比如字符串等
res.sendFile(path [, options] [, fn]):用于向浏览器写入文件,这里的path一定要是绝对路径才可以。
编写a.js:
var express = require('express');
var app = express();
app.get("/login",function(req,res){
res.sendFile(__dirname+"/test.html");
});
app.get("/listUsers",function(req,res){
res.send("用户列表");
});
app.listen(8080);
运行:
D:\zhao\nodews>node a
访问login:
访问listUsers:
__dirname:在nodejs中用于获取当前工作目录的绝对路径,非常方便。当然,你不使用__dirname而是使用fs模块来获取绝对路径也是可以的,最终都是殊途同归的。
var express = require('express');
var fs=require('fs');
var app = express();
app.get("/login",function(req,res){
fs.realpath("./test.html",function(err,data){
res.sendFile(data);
});
});
app.get("/listUsers",function(req,res){
res.send("用户列表");
});
app.listen(8080);
访问结果是一样的。
express的express.static方法处理静态资源
前边的程序已经可以向浏览器返送数据或者文件了,但是还有两个致命的问题没有解决:
①、当用户输入的请求路径不存在该怎么处理?
②、当向浏览器写入文件的时候,如果文件引入了外部的css、js、或者是图片,在页面里面并不能正常显示,又该怎么处理呢?
针对上面两个问题,express框架给出的解决方案分别是:
因为app.use就可以实现拦截所有请求路径的功能,所有针对第一个问题,我们可以用app.use来解决。
express.static(root, [options]):处理资源文件,root表示的静态资源文件所在的目录。
编写a.js:
var express = require('express');
var app = express();
//表示当前目录下面的public目录是静态资源文件所在目录
app.use(express.static('./public'));
app.get("/login",function(req,res){
res.sendFile(__dirname+"/test.html");
});
app.get("/listUsers",function(req,res){
res.send("用户列表");
});
//处理不存在的路径
app.use(function(req,res){
res.send("请求路径不存在");
})
app.listen(8080);
node a运行。
D:\zhao\nodews>node a
public的目录结构,在windows下可以使用tree /f命令查看目录结构:
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>用户登录</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script src="javascripts/demo_compiled.js"></script>
<style type="text/css">
</style>
</head>
<body>
<form>
<input type="text">
<input type="password">
<input type="button" value="登录">
</form>
<img src="imgs/logo.png">
<p>
我是登录页面
</p>
</body>
</html>
注意:这里引入资源文件的时候,不要再加入public了,因为public已经使用app.use(express.static('./public'));映射为资源文件所在目录了,因此,在html文件中引入的资源文件是目录是基于public的。
style.css:
p {
color: #4D926F;
}
demo_compiled.js:
console.log("我是外部引入的js文件");
启动之后,在浏览器中访问不存在的路径:
访问http://localhost:8080/login:
上面两个问题就解决了。
使用express-generator快速生产nodejs项目结构
安装:
D:\zhao\nodews>npm install -g express-generator
C:\Users\zhao\AppData\Roaming\npm\express -> C:\Users\zhao\App
Data\Roaming\npm\node_modules\express-generator\bin\express-cli.js
C:\Users\zhao\AppData\Roaming\npm
`-- express-generator@4.16.0
+-- commander@2.13.0
+-- ejs@2.5.7
+-- minimatch@3.0.4
| `-- brace-expansion@1.1.11
| +-- balanced-match@1.0.0
| `-- concat-map@0.0.1
+-- mkdirp@0.5.1
| `-- minimist@0.0.8
`-- sorted-object@2.0.1
安装好了express-generator之后,就可以使用命令快速生成nodejs项目的基本目录结构了:
D:\zhao\nodews>express project1
上面的命令表示把生成的目录结构放在proejct1这个文件夹下面,tree /f查看project1的目录结构:
生成完成。
按照package.json来下载相关依赖:
D:\zhao\nodews\project1>npm install
安装完成之后,使用命令npm start,即可启动这个生成的示例项目:
D:\zhao\nodews\project1>npm start
浏览器访问http://localhost:3000/,看到如下界面,ok:
访问会在后台打印日志:
D:\zhao\nodews\project1>npm start
> project1@0.0.0 start D:\zhao\nodews\project1
> node ./bin/www
GET / 200 1679.113 ms - 170
GET /stylesheets/style.css 200 8.379 ms - 111
GET /favicon.ico 404 64.803 ms - 1052
GET /users 200 2.854 ms - 23
express里的session使用
nodejs里面并不包含session模块,需要自行使用npm安装第三方支持session的模块,一般我都是用express-session.
npm install express-session --save-dev
我们使用这个插件来测验一下session,编写a.js:
let express = require('express');
let session=require('express-session');
var app=express();
//这里相当于初始化一下session,不加这一句后边的request.session依然是undefined
app.use(session({
secret: 'secretstring',
cookie: {maxAge:20*1000},
resave:false,
saveUninitialized:true
}));
//打印一下session
app.use(function(req,res,next){
console.log(req.session);
next();
});
app.get("/login",function(request,response){
//设置session的值
request.session.username='zhao';
request.session.pwd='123456';
response.end("login success");
});
app.get("/",function(request,response){
if(!request.session){
response.end("no auth!");
}else{
if(request.session.username=='zhao'&&
request.session.pwd=='123456'){
response.end("index");
}else{
response.end("not equal");
}
}
});
app.listen(8888);
console.log('正在监听8888端口');
运行node a,访问并查看控制台的输出:
使用session成功,注意啊,这里不能使用nodejs原生的request和response,要使用经过express框架包装过的request和response。
express-session的配置项解释:
name: 设置 cookie 中,保存 session 的字段名称,默认为 connect.sid 。
store: session 的存储方式,默认存放在内存中,也可以使用 redis,mongodb 等。express 生态中都有相应模块的支持。
secret: 通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改。
cookie: 设置存放 session id 的 cookie 的相关选项,默认为 (default: { path: ‘/’, httpOnly: true, secure: false, maxAge: null })
genid: 产生一个新的 session_id 时,所使用的函数, 默认使用 uid2 这个 npm 包。
rolling: 每个请求都重新设置一个 cookie,默认为 false。
resave: 即使 session 没有被修改,也保存 session 值,默认为 true。
cookie的相关属性:
name=value:键值对,要保存的值
Expires: 过期时间(秒),在设置的某个时间点后该 Cookie 就会失效
maxAge: 最大失效时间(毫秒),设置在多少后失效
secure: 当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效
path: 表示 cookie 影响到的路,如 path=/。如果路径不能匹配时,浏览器则不发送这个Cookie
httpOnly:布尔值,如果在COOKIE中设置了“httpOnly”属性,则前端js脚本将无法访问该cookie,此时只能通过服务端来对cookie访问
express使用connect-redis持久化session信息到redis
在“express里的session使用”这篇教程里面我使用了express-session包来存储session,默认的session是保存在服务器端的内存里面的,一般在项目里面我们都是把session存储在redis或者mogodb之中,express结合connect-redis包持久化session信息到redis里面。
首先安装connect-redis:
D:\zhao\nodews>npm install connect-redis --save-dev
安装完成之后package.json也新增了响应的依赖。
修改“express里的session使用”“”这篇教程里面的代码加入持久化session的代码:
let express = require('express');
let session=require('express-session');
let redisStore=require('connect-redis')(session);
var app=express();
//这里相当于初始化一下session,不加这一句后边的request.session依然是undefined
app.use(session({
store:new redisStore({
port: 6379, // Redis port
host: '192.168.28.113', // Redis host
db: 1
}),
secret: 'secretstring',
cookie: {maxAge:20*1000},
resave:false,
saveUninitialized:true
}));
//打印一下session
app.use(function(req,res,next){
console.log(req.session);
next();
});
app.get("/login",function(request,response){
//设置session的值
request.session.username='zhao';
request.session.pwd='123456';
response.end("login success");
});
app.get("/",function(request,response){
if(!request.session){
response.end("no auth!");
}else{
if(request.session.username=='zhao'&&
request.session.pwd=='123456'){
response.end("index");
}else{
response.end("not equal");
}
}
});
app.listen(8888);
console.log('正在监听8888端口');
相比之前的代码,本次做的更改如下红框中所示:
sotre的解释参见上面的解释,connect-redis的配置也是个对象,上面用到了三个:
port:要连接的redis的端口
host:要连接的reids所在机器的ip
db:把session信息放到redis的哪个数据库里面
其他还有可以使用的属性参见connect-redis在npmjs上的介绍https://www.npmjs.com/package/connect-redis。
到redis上查看:
nodejs使用express的res.json()返回json
nodejs中使用原生的http内置模块返回json的话比较麻烦,我们使用nodejs开发web项目,大多数的时候都在使用express框架,返回json数据(json对象和json数组),我们来看看如何使用express框架返回json。
先看看使用http内置模块返回json,我想当然的使用response.json("{'data':'ok'}")来返回:
var server=http.createServer(function (request, response) {
if(request.url=='/list'){
response.json("{'data':'ok'}");
}else{
response.writeHead(200, {'Content-Type': 'text/html'});
response.end('<h1>Hello World</h1> nodejs');
}
});
server.listen(8888);
报错:
TypeError: response.json is not a function
at Server.<anonymous> (D:\zhao\nodews\a.js:23:12)
at emitTwo (events.js:106:13)
at Server.emit (events.js:191:7)
at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:546:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)
说明,你这个想当然是错误的,为了方便我们使用express框架来返回json:
var express = require('express');
var app = express();
app.use("/list",function(req,res){
res.json("{'data':'ok'}");
});
app.use(function(req,res){
res.send('<h1>Hello World</h1> nodejs');
});
app.listen(8888);
console.log('正在监听8888端口');
运行结果:
我们知道,express是nodejs体系里的框架,它的请求对象req和响应对象res与nodejs内置模块中的请求对象req和响应对象res是不同的,express的对象包装了后者,因此用法也是不尽相同的。