一、express的简单介绍与安装
ndoe.js,一个基于javsscript的服务器环境,它的出现使得javascript有能力去实现服务器操作。
而基于node.js的Express则把原先的许多操作变的简单灵活,一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。使用 Express 可以快速地搭建一个完整功能的网站。
express官网
Express的安装
其实express的安装可以省略,因为 可以用express的脚手架来生成这一切
cnpm install express --save // 安装
express --version // 查看express是否安装成功,如果显示版本号,则安装正确
Express脚手架的安装
方法1、使用express-generator安装
npm install express-generator -g // 安装
express -h // 查看命令行的指令含义
// 结果:
Options:
--version 输出版本号
-e, --ejs 添加对 ejs 模板引擎的支持
--pug 添加对 pug 模板引擎的支持
--hbs 添加对 handlebars 模板引擎的支持
-H, --hogan 添加对 hogan.js 模板引擎的支持
-v, --view <engine> 添加对视图引擎(view) <engine> 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎)
--no-view 创建不带视图引擎的项目
-c, --css <engine> 添加样式表引擎 <engine> 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件)
--git 添加 .gitignore
-f, --force 强制在非空目录下创建
-h, --help 输出使用方法
express --view=ejs app // 创建了一个名为 app 的 Express 应用,并使用ejs模板引擎
cd myapp // 进入app目录
npm install // 安装依赖
set DEBUG=app:* & npm start // 在Windows 下,使用该命令启Express应用
DEBUG=app:* npm start // 在 MacOS 或 Linux 下,使用该命令启Express应用
最后输出结果:
// app文件内的目录:
bin: 启动目录 里面包含了一个启动文件 www 默认监听端口是 3000 (直接node www执行即可)
node_modules:依赖的模块包
public:存放静态资源
routes:路由操作
views:存放ejs模板引擎
app.js:主文件
package.json:项目描述文件
方法2:使用 express 命令 来快速从创建一个项目目录
express app -e // express 项目文件夹的名字 -e
cd app // 使用命令行进入项目目录
cnpm install // 安装依赖
二、Express路由简介与路由使用
路由简介
路由表示应用程序端点 (URI) 的定义以及响应客户端请求的方式。它包含一个请求方法(methods)、路径(path) 和 路由匹配时的函数(callback),如下
app.methods(path, callback);
app.get('/', (request, response) => { // 举例
response.send('<h1>hi,there</h1>')
})
路由方法
Express方法源于 HTTP 方法之一,附加到 express 类的实例。它可请求的方法包括:
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。
Express路径与动态路由
Express路径包含三种表达形式,分别为字符串、字符串模式、正则表达式
1.字符串路径
app.get("/login",function(req,res){
res.send("hi ,there!");
})
此路径地址将与/login匹配
2.字符串模式路径(类似于正则表达式模式)
此路由路径将与acd
和abcd
相匹配。
app.get('/ab?cd', function (req, res) {
res.send('ab?cd')
})
这条路线的路径将会匹配abcd
,abbcd
,abbbcd
,这里只需要以ab开头,cd结尾即可。
app.get('/ab+cd', function (req, res) {
res.send('ab+cd')
})
这条路线的路径将会匹配abcd
,abxcd
,abRANDOMcd
,ab123cd
,等。
app.get('/ab*cd', function (req, res) {
res.send('ab*cd')
})
此路由路径将与/abe
和相匹配/abcde
。
app.get('/ab(cd)?e', function (req, res) {
res.send('ab(cd)?e')
})
3.正则表达式路径
此路由路径将匹配其中带有a
的任何内容,因为路径 带有/ 所以需要使用转义
app.get(/\/a/, function (req, res) {
res.send('home')
})
这条路线的路径将匹配butterfly
和dragonfly
app.get(/.*fly$/, function (req, res) {
res.send('/.*fly$/')
})
不过在使用正则表达式里面的特殊字符时需要使用转义
4.动态路由
路由参数被命名为URL段,用于捕获URL中在其位置处指定的值。捕获的值将填充到req.params
对象中,并将路径中指定的route参数的名称作为其各自的键。
Request URL: http://localhost:3000/users/34/books/8989
Route path: /users/:userId/books/:bookId // 匹配的URL段
req.params: { "userId": "34", "bookId": "8989" } // 获得的对象
Request URL: http://localhost:3000/profile/userlihua
Route path: /profile/user:name
req.params: { "name": "lihua" }
要使用路由参数定义路由,只需在路由路径中指定路由参数,如下所示。
app.get('/users/:userId/books/:bookId', function (req, res) {
res.send(req.params)
})
路径参数的名称必须由“文字字符” ([A-Za-z0-9_]) 组成。
由于连字符(-
)和点(.
)是按字面解释的,因此可以将它们与路由参数一起使用
Request URL: http://localhost:3000/flights/LAX-SFO
Route path: /flights/:from-:to
req.params: { "from": "LAX", "to": "SFO" }
Request URL: http://localhost:3000/plantae/Prunus.persica
Route path: /plantae/:genus.:species
req.params: { "genus": "Prunus", "species": "persica" }
要更好地控制可以由route参数匹配的确切字符串,可以在括号()
中附加一个正则表达式:
Route path: /user/:userId(\d+)
Request URL: http://localhost:3000/user/42
req.params: {"userId": "42"}
由于正则表达式通常是文字字符串的一部分,因此请确保使用\
对其他特殊字符进行转义
在Express 4.x中,不以常规方式解释正则表达式中的*
字符。解决方法是使用{0,}
代替*
。这可能会在Express 5中修复。
路线处理程序
此程序是在路由上施加先决条件,如果在没有使用next的情况下,则将不会把路由的控制权传递给后续回调函数。
路由处理程序可以采用函数,函数数组或二者组合的形式,如以下示例所示。
单个回调函数可以处理路由。例如:
app.get('/example/a', function (req, res) {
res.send('Hello from A!')
})
多个回调函数可以处理一条路由(确保指定了next
参数)。例如:
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the 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])
独立功能和功能数组的组合可以处理路由。例如:
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
app.get('/example/d', [cb0, cb1], function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from D!')
})
response方法
这里的res就是response
路由模块
以下示例将路由器创建为模块,在其中加载中间件功能,定义一些路由,并 将路由器模块安装在主应用程序的路径上。
在birds.js
下:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next() // 必须调用next方法
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
然后,在应用程序(app.js)
中加载路由器模块:
var birds = require('./birds')
// ...
app.use('/birds', birds)
该应用程序现在将能够处理对/birds
和/birds/about
的请求,以及调用timeLog
特定于该路线的中间件功能。
app.set(name, value)
该方法的目的是给name赋值,也就是给变量赋值
app.set('title', 'My Site') // 通过set方法赋值
app.get('title') // "My Site" // 通过get方法取值
三、express的ejs模板
简介
相比于jade模板引擎,ejs对原HTML语言就未作出结构上的改变,只不过在其交互数据方面做出了些许修改,相比于jade更加简单易用。因此其学习成本是很低的。
您也可参考ejs官网
ejs基本使用
app.js文件:
const express=require("express");
const ejs=require("ejs"); // 引入ejs
const fs=require("fs");
var app=express();
app.set('views', path.join(__dirname, 'views')); // 设置视图的对应目录,前一参数为变量名(一般取为视图的文件名),后一参数为视图的路径
app.set("view engine","ejs"); // 设置默认的模板引擎
app.engine('ejs', ejs.__express); // 定义模板引擎
app.get("/",function(req,res){
res.render("index.ejs",{title: "<h4>express</h4>"}); // 使用render来渲染模板,且要注意这里面的第二个参数必须为对象
});
ejs文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<% for(var i=0;i<10;i++){ %> // js的语法
<%= i %> // 输出为:0 1 2 3 4 5 6 7 8 9
<% } %>
<% if(i == 10) { %> // 为true
<h1>i == 10</h1> // 输出为:i == 10
<h1>
<%- i %> //输出为:10
</h1>
<% } %>
<!-- 获取变量 -->
<div class="datas">
<p>获取变量:</p>
<%- title %> // 输出非转义的数据到模板,结果为:express的h4标签
<%= title %> // 输出数据到模板(数据是转义 HTML 标签),结果为:<h4>express</h4>
</div>
</body>
</html>
这时我们会得到如下图的结果:
由此可以知道:
<% xxx %>:里面写入的是js语法,
<%= xxx %>:里面接受服务端发送给ejs模板的变量,不会解析变量里面的值(带<>符号),之所以输出类似与<h1>xxx</h1>,是因为对变量里的值(<>)进行了转义
<%- xxx %>:里面接受服务端发送给ejs模板的变量,会解析变量里面的值(会解析<>),输出XXX
<%# 注释标签,不执行、不输出内容
ejs标签各种含义
<% '脚本' 标签,用于流程控制,无输出。
<%_ 删除其前面的空格符
<%= 输出数据到模板(输出是转义 HTML 标签)
<%- 输出非转义的数据到模板
<%# 注释标签,不执行、不输出内容
<%% 输出字符串 '<%'
%> 一般结束标签
-%> 删除紧随其后的换行符
_%> 将结束标签后面的空格符删除
另外res.render()函数也是支持回调的,例如:
res.render('user', { name: 'Tobi' }, function(err, html) {
console.log(html);
});
另外值得说明的是ejs模块也有ejs.render()和ejs.renderFile()方法,他在这里与res.render()作用类似,如下:
ejs.render(str, data, options);
ejs.renderFile(filename, data, options, function(err, str){
// str => 输出绘制后的 HTML
});
四、express内的get请求
关于get一些说明
一般在网站开发中,get都用作数据获取和查询,且在发送请求后,可通过req.query对象可以获得用户发送的数据
实例
app.get("/login",function(req,res){
console.log(req.query);
res.send("登录路由,user为:"+req.query.username+"==> password为:"+req.query.password);
});
五、express内的POST请求
关于post一些说明
相比于get请求,post所请求的数据会更加安全。
get请求会在地址栏显示输入的用户名和密码(有中文时会转化为BASE64加密),而post请求则会将数据放入http包的包体中,这使得别人无法直接看到用户名和密码!
我们的知道,首先我们得知道在form表单进行post请求,enctype属性一般设置为 “application/x-www-form-urlencoded”,如果设置成multipart/form-data,则多用于文件上传
app.use(express.urlencoded({ extended: false }))// 设置解析中间件,用来解析post提交的数据
req.body.username // 获取post请求的数据
案例:
app.use(express.urlencoded({ extended: false }))
//处理登陆请求
app.post('/login',async (req,res)=>{
console.log(req.body);
res.send("登录路由,user为:"+req.body.username+"==> password为:"+body.query.password);
})
六、express内的中间件
从字面意思,我们可以了解到它大概就是做中间代理操作,事实也是如此;大多数情况下,中间件就是在浏览器发送请求和服务器发送响应中间的一系列操作。
事实上,express是一个路由和中间件的web框架
中间件函数 可以执行以下任务:执行任何代码;对请求和响应对象进行更改;结束请求/响应循环;调用堆栈中的下一个中间件函数。
中间件也分为应用层中间件、路由中间件、内置中间件、错误处理中间件和第三方中间件,且中间件自带两个参数(request、response)
下面分别对以下进行说明:
1.应用层中间件
应用级中间件绑定到app对象使用app.use和app.METHOD()
app.use([path,] callback [, callback…]) 是指定的一个或多个中间件函数安装在指定的路径上:当所请求路径的基数与path匹配时,将先执行中间件函数(截获),且当参数path省略时则默认为\
(即访问任何页面,函数都会被调用)
而app.METHOD(path, callback [, callback …])用于处理http请求,例如GET、PUT、POST,其实际的方法就是app.get()、app.post()、app.put()
例如下面实例:
app.use(function(req,res,next){
console.log("访问任何页面,该函数都会被调用,且会在app.get()、app.post()等等之前被调用");
next();
});
不过要注意的是如果不调用next方法那么地址会一直加载,因为我们在中间件里截取了函数,而如果要解决这个问题的话则需要调用next方法
2.路由中间件
路由级中间件和应用级中间件类似,只不过他需要绑定express.Router([options]),且他是为了配置复杂路由
var router = express.Router()
在匹配路由时,我们使用 router.use() 或 router.VERB() ,路由中间件若多次结合callback则可用于用户登录及用户状态检测。
例如:
// profile.js文件
var express = require('express');
var router=express.Router();
router.use("/user",function(req,res,next){
console.log("分类:",req.originalUrl);
next();
},function(req,res){
res.send("JavaScript");
});
module.exports = router;
// app.js文件
var app = express(); // 创建app对象
var indexRouter = require('./routes/index');
app.use("/category",router); // 配置路由界面
3.错误处理中间件
顾名思义,它是指当我们匹配不到路由时所执行的操作。错误处理中间件和其他中间件基本一样,只不过其提供了4个自变量参数。
var createError = require('http-errors');
// 处理404的中间件
app.use(function(req, res, next) {
// 可以用该res.render('404.html');语句来生成好看的404页面,但是下面的next必须注释,
// 且生成的404页面就会盖住错误信息
next(createError(404));
});
// 处理错误的中间件
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');
});
一般情况下,我们把错误处理放在最下面,这样我们即可对错误进行集中处理。
4.内置中间件
从版本4.x开始,Express不再依赖Content,也就是说Express以前的内置中间件作为单独模块,express.static是Express的唯一内置中间件。
express.static(root, [options]);
通过express.static我们可以指定要加载的静态资源。
5.第三方中间件
它采用引入 外部模块 的方式来获得更多的应用操作。如后期的cookie和session。
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
七、nodemon与pm2
nodemon
nodemon可在检测到目录中的文件更改时通过 自动重新启动节点应用程序来帮助开发基于node.js的 应用程序(热刷新) ,不过他是用于 开发阶段的工具
npm install -g nodemon // 安装
// nodemon的使用
node main.js // 启动文件,不能热刷新
nodemon main.js // 启动文件,可以热刷新
pm2
PM2 是一个类似于Nodemon的工具,不同之处在于它用于生产环境,和Nodemon相似的地方在于他会监控你的app的任何修改或者重新部署,但是有更好的一面, PM2 在程序遭遇到崩溃的时候,他会正确重启你的app,并且pm2会在后台运行
npm install pm2 -g // 安装
// pm2的使用
pm2 start main.js
八、app.js文件的说明
该网站目录访问顺序**:匹配/ => 匹配/users => 匹配/(默认)**
过程中只要一render则顺序链被打断 ,所以说app.js文件的执行是严格从上到下的
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index'); // 导入首页路由
var usersRouter = require('./routes/users'); // 导入用户路由
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views')); // 添加视图路径
app.set('view engine', 'ejs'); // 设置视图模板的引擎
app.use(logger('dev')); // 开发时的日志
app.use(express.json()); // 服务器端的JSON解析
app.use(express.urlencoded({ extended: false })); // 设置POST数据的解析
app.use(cookieParser()); // 设置cookie的解析
app.use(express.static(path.join(__dirname, 'public')));// 设置静态文件的解析
app.use('/', indexRouter); // 首页路由
app.use('/users', usersRouter); // 用户路由
// 处理404的中间件,且要注意这里的顺序是在其他的路由之后的
app.use(function(req, res, next) {
// 可以用该res.render('404.html');语句来生成好看的404页面,但是下面的next必须注释,
// 且生成的404页面就会盖住错误信息
next(createError(404));
});
// 处理错误的中间件
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.ejs');
});
module.exports = app;
本文只用于个人学习与记录