NodeJS入门实践第二讲Express的使用
NodeJs提供了的方法处理起来比较麻烦,在使用nodejs时有很多现成的框架帮助开发人员,其中最简单的一个server框架就是Express,express的安装方式有两种,一种是在项目文件夹中安装express,另外一个是通过express的generator来生成一个express的骨架,我们先从最原始的方式来了解express中的几个比较核心的概念
基于原始的方式使用express
首先创建一个项目,之后初始化为npm类型的项目,要生成node的基本项目,需要使用npm init 进行初始化,初始化会输入一些基本的项目信息,这个步骤和java的maven非常类似,等于创建了maven的pom文件,而对于node而言是创建了package.json文件。
下一步使用npm安装express,由于express仅仅只是存在在当前项目中,所以安装不用使用-g进行全局安装,但是需要增加–save的参数,这个表示会把这些信息添加到package.json的依赖中。
npm install express --save
大家会发现package.json中有了一个dependencies,添加了express的依赖。在这个文件我进行两个设置,在scripts中增加了start的脚本,该脚本使用node app.js来运行一个文件,此时app.js是我们的核心的服务器文件,添加了这个脚本之后,可以直接使用npm start 运行该脚本。
下来创建app.js来运行一个express的应用
var express = require("express")
var app = express();
app.get("/hello",function (req,resp) {
resp.send("hello express!");
});
app.listen(3000,function () {
console.log("server start!!");
});
已上程序中头两行创建了express的对象,app.get(xx)这个和nodejs的路由一模一样,表示接收了get请求,之后使用function回调函数进行处理,通过resp的send方法可以输出数据到网页,app.listen(xx)表示监听端口,等于nodejs中的createServer。已上程序只要运行之后在浏览器中输入localhost:3000/hello将会显示hello express的文本在浏览器中。这就是一个简单的express的应用,注意express是一个完全基于路由的架构,下面我们来详细了解express中最核心的一个概念:中间件。
express的中间件
express是一个自上而下进行执行的运用,在具体讲解之前,我们加入一个应用包,我们现在的程序只要改动app.js都得开发人员自动关闭和启动nodejs。在npm中有一个叫做nodemon的框架,可以帮助我们自动重新启动,nodemon建议大家安装成全局类型,这样方便所有的应用都能使用,使用下面的脚本安装
npm install nodemon -g
安装完成之后只要通过nodemon app.js此时只要项目中的文件有变化它会自动启动,为了方便这个操作,我将package.json中start的脚本进行的了简单的修改
....
"scripts": {
"start": "nodemon app.js"
},
...
下面我们来修改app.js中的一些代码
...
app.get("/:username",function(req,resp){
resp.send("hello:"+req.params.username);
});
app.get("/hello",function (req,resp) {
resp.send("hello express!");
});
...
第一个get表示获取/xxx,这个路径会被解析成为username的参数,通过req可以显示这个参数。此时当我们输入路径localhost:3000/hello,express在读取代码的时候会首先匹配到,它会将hello认为是username,所以浏览器中会显示hello:hello 第二个/hello的路由就不会再被匹配了,express的执行方式就是这样的。只要匹配到一个路由,如果没有做任何特殊的处理,处理完就停止了。
上面我们提到的特殊处理就是中间件的一种形式,如果我们在第一个函数中增加一个next的参数,就能够做一些特殊处理,大家看代码
app.get("/:username",function(req,resp,next){
// resp.send("hello:"+req.params.username);
console.log(req.params.username);
next();
});
app.get("/hello",function (req,resp) {
resp.send("hello express!");
});
此时第一个不使用send,而是使用console.log输出了一下username。执行完成之后使用了next(),这表示express会继续向下执行请求,所以第二个路由/hello也会被执行,所以将会输出hello express。这就是express中间件的一个核心操作方式,用户可以通过next()来确定路由请求是否往下提交。下面我们来看express另一个重要的函数,use函数。
开始我们已经多次强调了express的所有操作都是基于路由进行的,其中get方法处理get请求,post方法处理post请求,而还有一个特殊的use方法,也是处理请求,但它的特殊性在于,它会自动匹配满足条件的所有请求,首先就刚才讲的例子来看,我们的地址如果是localhost:3000/hello/abc。此时有两个路径,express一个都不会进行匹配。
此时如果将get改成use,express会自动匹配所有/hello开头的请求
app.use("/hello",function (req,resp) {
resp.send("hello express!");
});
我们发现,只要路径是以/hello开头的所有路径都会被匹配如: /hello/abc,/hello/abc/a/b等等,这些都会被匹配。此时如果有个地址是app.use("/",function(req,resp)) 那是不是意味着所有的请求都会通过这个函数,此时如果不使用next,那意味着,请求到这个就终止了,但是我们可以通过next将请求往下执行。如果地址是/,我们可以省略第一个参数使用app.use(function(req,resp)) 来替换。
以上操作就提供了一种方式,让我们可以让express执行我们的某个模块,这个模块我就将其称之为中间件,接下来我们自己编写一个处理静态资源文件的中间件。
var express = require("express");
var url = require("url");
var fs = require("fs");
var app = express();
// app.get("/:username",function(req,resp){
// resp.send("hello:"+req.params.username);
// });
function handleStatic(req,resp,next) {
var pathname = url.parse(req.url).pathname;
if(pathname=="/") pathname = "index.html";
fs.readFile("./publics/"+pathname,null,function (err,data) {
if(err) {
//文件不在,直接next到后面的请求
next();
} else {
resp.writeHead(200,{"Content-type":"text/html;charset=utf-8"});
resp.write(data);
resp.end();
}
});
}
app.use(handleStatic);//启动handleStatic的函数,所有请求都会调用
app.get("/:username",function(req,resp,next){
// resp.send("hello:"+req.params.username);
console.log(req.params.username);
next();
});
app.use("/hello",function (req,resp) {
resp.send("hello express!");
});
app.listen(3000,function () {
console.log("server start!!");
});
handleStatic就等于一个我们自定义的中间件。通过这个例子我相信大家对express的处理流程已经有了一个清晰的理解了,这种中间件的方式为我们的代码带来了极大的灵活性,我们可以非常轻松的往express中添加和删除各种模块。
使用express-generator创建项目
现在我们应该已经对express有了大致的了解,下面就可以使用express-generator来生成express项目了,这将会极大的简化我们的开发操作,首先将express-generator安装到全局中。
- npm install express-generator -g
安装完成之后使用
- express 13_express
此时会完成express骨架的创建,首先看看package.json文件
看看scripts中的start脚本,表示服务器的启动文件不是app.js而是bin路径中的www文件,我们可以将其修改为nodemon ./bin/www,之后看看依赖包,首先body-parser用来解析http请求的,可以非常轻松的解析json数据格式;cookie-parse用来解析cookie请求,debug用来进行调试,jade就是模板引擎,可以将其修改为handlebars,morgan是日志组件,serve-favicon用来处理图标,此时这些包并没有安装到我们的项目中的,需要使用install进行安装
npm install
之后移除jade,可以安装pug
npm remove jade --save
npm install pug --save
看看最重要的app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//定义了两个路由模块
var index = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
//静态文件的处理,所有的静态文件在public中
app.use(express.static(path.join(__dirname, 'public')));
//路由处理,/会交给index模块处理
app.use('/', index);
// users开头的会交给user模块处理
app.use('/users', users);
//异常处理
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
cookie-parser和body-parser模块
var express = require("express");
var bodyParser = require("body-parser");
// var app = require("./app");
var app = express();
app.use(bodyParser());
app.get("/",function(req,resp) {
// resp.sendFile("index.html",{root:__dirname+"/publics"});
resp.sendFile("index.html",{root:__dirname});
});
app.listen(3000,function (req,resp) {
console.log("server start!");
});
app.post("/form",function (req,resp) {
//使用req.body来获取name为username的值
resp.send(req.query.hello+","+req.body.username);
// resp.send(req.query.hello);
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
Hello app!
<form action="/form?hello=abc" method="post">
<input type="text" name="username"/><br/>
<input type="submit"/>
</form>
</body>
</html>
程序中通过req.query来获取浏览器的get参数,我们会发现req.query.username取不到任何值,这说明req.query仅仅只能获取get的请求参数,那该如何获取post的请求参数呢?此时就需要使用body-parser的中间件来执行。首先安装body-parser中间件
npm install body-parser --save
此时form中的内容就可以通过req.body来读取。通过这个实例大家应该清楚body-parser的作用了,接下来我们来看看如何处理cookie,cookie和jsp中的cookie是一样,我们需要cookie-parser模块支持,首先安装cookie-parser的中间件。
npm install cookie-parser --save
var express = require("express");
var bodyParser = require("body-parser");
var cookieParser = require("cookie-parser");
var session = require("express-session");
var app = express();
app.use(bodyParser());
//使用cookie-parser的中间件。
app.use(cookieParser());
app.use(session({
secret: 'a4f8071f-c873-4447-8ee2'
}));
//基于get请求的login
app.get("/login",function(req,resp){
resp.sendFile("login.html",{root:__dirname});
});
//基于post请求的login
app.post("/login",function (req,resp) {
var username = req.body.username;
var password = req.body.password;
if(username=="admin"&&password=="123") {
//存储了cookie,时间是60分钟
resp.cookie("user",{username:username,password:password},{maxAge:600000,httpOnly:true});
}
resp.redirect("/loginOk");
});
app.get("/loginOk",function (req,resp) {
var cookies = req.cookies.user;
if(cookies) {
resp.send("hello:"+cookies.username);
} else {
resp.send("no cookies found!");
}
});
app.get("/session",function (req,resp) {
req.session.username = "admin";
req.session.nickname = "超级管理员";
resp.redirect("/sessionOk");
});
app.get("/sessionOk",function (req,resp) {
resp.send("session ok:"+req.session.username+"("+req.session.nickname+")");
});
app.listen(3000,function (req,resp) {
console.log("server start!");
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
<form action="/login" method="post">
username:<input type="text" name="username"/><br/>
password:<input type="password" name="password"/><br/>
<input type="submit"/>
</form>
</body>
</html>
如果用户名和密码正确通过resp.cookie方法存储cookie,cookie的名称是user,存储了一个username和password的对象,有效时间是60分钟,然后是基于http请求的存储。最后通过resp.redirect("/loginOk"),这其实就是jsp中的服务器跳转。最后看看loginOk是如何读取cookie的
在这一小节结束之前我们需要再次总结req获取参数的三种方式:
1、req.params.xx 这是获取路径中的参数
2、req.query.xx 这是获取get请求的参数
3、req.body.xx 这是通过body-parser来获取form表单中的post请求。
express的session
express同样也支持session,需要express-session的支持,首先通过npm安装express-session
npm install express-session --save
secret是一个服务器端的签名,这个字符串可以随便设定,之后通过req.session来写和读取session,session的操作非常简单
通过/session的路由来设定session,在sessionOk中来读取session的值。session如果没有设定特殊的cookie的值,关闭浏览器就失效。