express学习 (1)
环境配置与第一个express项目
【任务】:
- 安装express;
- 创建第一个express项目;
- 掌握express项目结构;
- 了解expres路由原理;
- 了解express视图以及路由与视图的关系;
目 录
4. 初识express 路由Router
与 视图views
1. 安装Express
1.1 通过npm的安装方式
npm install -g express-generator
1.2 通过yarn的安装方式
yarn global add express-generator
2. 创建一个名为myproject
的项目
express myproject
运行命令如图所示,可以看到执行完成后,目录中多了一个名为"myproject"的新项目,即我们初创的项目文件夹:
该项目目录的初始结构如下图所示:
3. 初始的 应用主文件app.js
其内容与含义注释如下:
var createError = require('http-errors'); // http错误处理模块 var express = require('express'); // express模块 var path = require('path'); // path 模块 var cookieParser = require('cookie-parser'); // cookie处理模块 var logger = require('morgan'); // 日志管理模块 var indexRouter = require('./routes/index'); // 路由目录中的`index.js`文件 var usersRouter = require('./routes/users'); // 路由目录中的`user.js`文件 var app = express(); // 创建express应用实例 // view engine setup app.set('views', path.join(__dirname, 'views')); // 定义页面目录 app.set('view engine', 'jade'); // 定义页面模板引擎 app.use(logger('dev')); // 定义日志打印级别 app.use(express.json()); // 定义JSON格式处理数据 app.use(express.urlencoded({ extended: false })); // 定义使用urlencode处理数据集querystring模块解析数据 app.use(cookieParser()); // 定义使用cookie处理对象 app.use(express.static(path.join(__dirname, 'public'))); // 定义静态资源目录 app.use('/', indexRouter); // 定义指向index.js app.use('/users', usersRouter); // 定义指向users.js app.use(function(req, res, next) { // 定义404错误处理 next(createError(404)); }); app.use(function(err, req, res, next) { // 定义其它错误处理 // 设置 locals,只在开发环境生效 res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; res.status(err.status || 500); // 返回http状态码 res.render('error'); // 渲染错误页面 }); module.exports = app;
这里的app
是一个express实例,有几个关于app的函数含义如下:
3.1 app.use
在指定的路径挂载指定的一个或多个中间件函数:当请求路径的基与path匹配时,执行中间件函数。
app.use([path,] callback [, callback...])
参数
参数 | 描述 | 默认值 |
path |
调用中间件功能的路径;可以是以下任何一个:表示路径的字符串。路径模式。匹配路径的正则表达式模式。上述任意组合的数组。 | |
callback |
回调函数;可以是:一个中间件功能。一系列中间件功能(用逗号分隔)。一系列中间件功能。以上所有内容的组合。您可以提供多个行为类似于中间件的回调函数,除了这些回调可以调用next(“route”)来绕过剩余的route回调。您可以使用这种机制对路线施加先决条件,如果没有理由继续当前路线,则将控制权交给后续路线。由于路由器和应用程序实现了中间件接口,您可以像使用任何其他中间件功能一样使用它们 | None |
描述
路由将匹配任何紧跟其路径的路径,并带有“/”。例如:app.use('/apple', ...)
将匹配“/apple”
、“/apple/images”
、“/apple/images/news”
等等。
由于路径默认为"/"
,所以没有路径的中间件会为应用程序的每个请求执行。
例如,这个中间件功能将对应用程序的每个请求执行:
app.use(function (req, res, next) { console.log('Time: %d', Date.now()) next() })
中间件功能是按顺序执行的,因此中间件包含的顺序很重要。
// this middleware will not allow the request to go beyond it app.use(function (req, res, next) { res.send('Hello World') }) // requests will never reach this route app.get('/', function (req, res) { res.send('Welcome') })
错误处理中间件
错误处理中间件总是接受四个参数。 您必须提供四个参数来将其标识为错误处理中间件函数。即使不需要使用下一个对象,也必须指定它来维护签名。否则,下一个对象将被解释为常规中间件,并且无法处理错误。 关于错处理中间件的更多细节可以参考错误处理官方文档。
以与其他中间件功能相同的方式定义错误处理中间件功能,除了用四个参数而不是三个,特别是用签名 (err, req, res, next)
):
app.use(function (err, req, res, next) { console.error(err.stack) res.status(500).send('Something broke!') })
Path 例子
下表提供了一些用于安装中间件的有效Path
值的简单示例。
类型 | 例子 |
Path | 这将匹配以 /abcd :app.use('/abcd', function (req, res, next) { next() }) 开头的路径 |
Path Pattern | 这将匹配以 /abcd 和 /abd :app.use('/abc?d', function (req, res, next) { next() }) 开头的路径。This will match paths starting with /abcd , /abbcd , /abbbbbcd , 等等:app.use('/ab+cd', function (req, res, next) { next() }) 这将匹配以 /abcd , /abxcd , /abFOOcd , /abbArcd , 等等:app.use('/ab*cd', function (req, res, next) { next() }) 开头的路径这将匹配以 /ad 和 /abcd :app.use('/a(bc)?d', function (req, res, next) { next() }) 开头的路径 |
Regular Expression | 这将匹配以 /abc 和 /xyz :`app.use(//abc |
Array | 这将匹配以 /abcd , /xyza , /lmn , 和 /pqr :`app.use([‘/abcd’, ‘/xyza’, //lmn |
中间件的回调函数例子
下表提供了一些简单的中间件函数示例,它们能用做app.use()
、 app.METHOD()
和 app.all()
的回调参数。
尽管这些例子是为 app.use()
准备的, 然而他们同时适合 app.use()
、 app.METHOD()
,和 app.all()
.
用法 | 例子 |
Single Middleware | 您可以在本地定义和安装中间件功能。app.use(function (req, res, next) { next() }) router是有效的中间件。 const router = express.Router() router.get('/', function (req, res, next) { next() }) app.use(router) 一个 Express 应用(app) 是合理的中间件。 const subApp = express() subApp.get('/', function (req, res, next) { next() }) app.use(subApp) |
Series of Middleware | 您可以在同一装载路径上指定多个中间件功能。const r1 = express.Router() r1.get('/', function (req, res, next) { next() }) const r2 = express.Router() r2.get('/', function (req, res, next) { next() }) app.use(r1, r2) |
Array | 使用数组对中间件进行逻辑分组。const r1 = express.Router() r1.get('/', function (req, res, next) { next() }) const r2 = express.Router() r2.get('/', function (req, res, next) { next() }) app.use([r1, r2]) |
Combination | 您可以将上述所有安装中间件的方式结合起来。function mw1 (req, res, next) { next() } function mw2 (req, res, next) { next() } const r1 = express.Router() r1.get('/', function (req, res, next) { next() }) const r2 = express.Router() r2.get('/', function (req, res, next) { next() }) const subApp = express() subApp.get('/', function (req, res, next) { next() }) app.use(mw1, [mw2, r1, r2], subApp) |
下面是一些在一个express app 里使用express.static 中间件的例子
从应用程序目录中的public
目录为应用程序提供静态内容:
// GET /style.css etc app.use(express.static(path.join(__dirname, 'public')))
仅当请求路径以“/static
”为前缀时,才在“/static
”处安装中间件以提供静态内容:
// GET /static/style.css etc. app.use('/static', express.static(path.join(__dirname, 'public')))
通过在静态中间件之后加载记录器中间件,禁用静态内容请求的日志记录:
app.use(express.static(path.join(__dirname, 'public'))) app.use(logger())
设置从多个目录提供静态文件,但优先考虑”./public
”:
app.use(express.static(path.join(__dirname, 'public'))) app.use(express.static(path.join(__dirname, 'files'))) app.use(express.static(path.join(__dirname, 'uploads')))
3.2 app.set
app.set(name, value)
将设置分配name给value。您可能会存储您想要的任何值,但只有确定的某些名称可用于配置服务器的行为。这些特殊名称列在应用程序设置表中。
为一个Boolean属性调用app.set('foo', true)
与 调用app.enable('foo')
一样。类似地,为一个Boolean属性调用app.set('foo', false)
和调用app.disable('foo')
一样
检索使用app.get()
设置的值。
应用设置
下表列出了应用程序设置。
请注意,子应用程序将:
- 不继承具有默认值的设置的值。您必须在子应用程序中设置该值。
- 继承没有默认值的设置值;下表明确指出了这些问题。
Exceptions: 子应用程序将继承“信任代理”的值,即使它有默认值(为了向后兼容);子应用程序在生产环境中不会继承“视图缓存”的值(当“NODE_ENV”为“生产环境”时)。
属性 | 类型 | 描述 | 默认 |
case sensitive routing |
Boolean | Enable case sensitivity. When enabled, “/Foo” and “/foo” are different routes. When disabled, “/Foo” and “/foo” are treated the same.NOTE: Sub-apps will inherit the value of this setting. | N/A (undefined) |
env |
String | Environment mode. Be sure to set to “production” in a production environment; see Production best practices: performance and reliability. | process.env.NODE_ENV (NODE_ENV environment variable) or “development” if NODE_ENV is not set. |
etag |
Varied | Set the ETag response header. For possible values, see the etag options table.More about the HTTP ETag header. |
weak |
jsonp callback name |
String | Specifies the default JSONP callback name. | “callback” |
json escape |
Boolean | Enable escaping JSON responses from the res.json , res.jsonp , and res.send APIs. This will escape the characters < , > , and & as Unicode escape sequences in JSON. The purpose of this it to assist with mitigating certain types of persistent XSS attacks when clients sniff responses for HTML.NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
json replacer |
Varied | The ‘replacer’ argument used by JSON.stringify .NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
json spaces |
Varied | The ‘space’ argument used by JSON.stringify . This is typically set to the number of spaces to use to indent prettified JSON.NOTE: Sub-apps will inherit the value of this setting. |
N/A (undefined) |
query parser |
Varied | Disable query parsing by setting the value to false , or set the query parser to use either “simple” or “extended” or a custom query string parsing function.The simple query parser is based on Node’s native query parser, querystring.The extended query parser is based on qs.A custom query string parsing function will receive the complete query string, and must return an object of query keys and their values. |
“extended” |
strict routing |
Boolean | Enable strict routing. When enabled, the router treats “/foo” and “/foo/” as different. Otherwise, the router treats “/foo” and “/foo/” as the same.NOTE: Sub-apps will inherit the value of this setting. | N/A (undefined) |
subdomain offset |
Number | The number of dot-separated parts of the host to remove to access subdomain. | 2 |
trust proxy |
Varied | Indicates the app is behind a front-facing proxy, and to use the X-Forwarded-* headers to determine the connection and the IP address of the client. NOTE: X-Forwarded-* headers are easily spoofed and the detected IP addresses are unreliable.When enabled, Express attempts to determine the IP address of the client connected through the front-facing proxy, or series of proxies. The req.ips property, then contains an array of IP addresses the client is connected through. To enable it, use the values described in the trust proxy options table.The trust proxy setting is implemented using the proxy-addr package. For more information, see its documentation.NOTE: Sub-apps will inherit the value of this setting, even though it has a default value. |
false (disabled) |
views |
String or Array | A directory or an array of directories for the application’s views. If an array, the views are looked up in the order they occur in the array. | process.cwd() + '/views' |
view cache |
Boolean | Enables view template compilation caching.NOTE: Sub-apps will not inherit the value of this setting in production (when NODE_ENV is “production”). |
true in production, otherwise undefined. |
view engine |
String | The default engine extension to use when omitted.NOTE: Sub-apps will inherit the value of this setting. | N/A (undefined) |
x-powered-by |
Boolean | Enables the “X-Powered-By: Express” HTTP header. | true |
trust proxy
设置地选项
类型 | 值 |
Boolean | If true , the client’s IP address is understood as the left-most entry in the X-Forwarded-* header.If false , the app is understood as directly facing the Internet and the client’s IP address is derived from req.connection.remoteAddress . This is the default setting. |
String String containing comma-separated values Array of strings | 信任的一个 IP 地址, 子网, 或者 一组(array) IP 地址, 和子网。 预配置子网名字为: loopback - 127.0.0.1/8 , ::1/128 linklocal - 169.254.0.0/16 , fe80::/10 uniquelocal - 10.0.0.0/8 , 172.16.0.0/12 , 192.168.0.0/16 , fc00::/7 以下面任意一种方式设置IP地址: 指定单个子网: app.set('trust proxy', 'loopback') 指定一个子网和一个地址: app.set('trust proxy', 'loopback, 123.123.123.123') 指定多个子网为CSV: app.set('trust proxy', 'loopback, linklocal, uniquelocal') 指定多个子网为一个数组: app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) 指定时,IP地址或子网被排除在地址确定过程之外,离应用服务器最近的不受信任的IP地址被确定为客户端的IP地址。 |
Number | 信任来自前端代理服务器的第n跳(hop)作为客户端。 |
Function | 自定义信任实现。只有在你知道自己在做什么的情况下才使用这个。`app.set(‘trust proxy’, function (ip) { if (ip === ‘127.0.0.1’ |
etag
设置地选项
NOTE: 这些设置仅适用于动态文件,不适用于静态文件。 express的静态中间件将忽略这些设置。
ETag 功能是使用etag包实现的
类型 | 值 |
Boolean | 如果为true 允许 weak ETag.(这是默认的设置)如果为 false 禁止所有 ETag . |
String | 如果为 “strong”, 允许 strong ETag. 如果为 “weak”, 允许 weak ETag. |
Function | 自定义ETag函数实现。 只有在你知道自己在做什么的情况下才使用这个。 app.set('etag', function (body, encoding) { return generateHash(body, encoding) // consider the function is defined }) |
4. 初识express 路由Router
与 视图views
4.1 路由文件
express在路由子目录routes
中自动为我们初始化了两个默认项目的路由文件:index.js 和 users.js:
index.js文件
var express = require('express'); // 引入express var router = express.Router(); // 引入express路由器对象 router.get('/', function(req, res, next) { // 定义一个请求方式为 GET 的路由,其路由值为'/',也就是首页。 res.render('index', { title: 'Express' }); // 路由交给名为`index`的视图模板文件进行渲染,并给视图闯入title参数值为`Express` }); module.exports = router; // 导出路由
users.js文件
var express = require('express'); var router = express.Router(); /* GET users listing. */ router.get('/', function(req, res, next) { res.send('respond with a resource'); }); module.exports = router;
我们也可以给路由文件起名为其它有某些含义的名字,但不论我们给路由文件起什么名字,定义一个路由一般有以下几个步骤:
- 引入express;
- 引入express路由器对象;
- 确定路由是什么,如根路由’/'表示首页;
- 确定该路由对应的请求方式是什么,如
GET
、POST
、DELETE
等等; - ★确定响应的返回对象,如
render()
方法对应的是一个渲染页面,最终的响应是一个通过结合视图文件中的模板渲染后的HTML页面字符串,而send()
方法直接发送一个内容作为HTTP响应。参考下表:
- 返回对象Response:
方法 | 描述 | 说明 |
Response.render() |
渲染页面 | 他有3个参数, · view (必须),是一个字符串,表示用于渲染的文件名(相对路径);· local (可选),是一个对象,为属性定义页面的局部变量;· callback (可选),是一个函数,为回调函数。返回可能的错误和呈现的字符串,但不执行自动响应。发生错误时,该方法在next(err) 内部调用。 |
Response.send() |
发送HTTP响应 | 该方法发送一个HTTP响应给请求端,可以接收一个参数,并且该参数可以是任何类型。比如一个Buffer对象、一个字符串、一个对象,或者一个数组。如res.send(new Buffer('hellow')); ,再如res.send('some string') ,又如res.send({key:'value'}) |
Response.json() |
返回JSON格式的字符串数据 | 例如res.json({name:'JACK'}) ,我们传入的是一个js对象,但通过json() 方法,它将自动地被转换为一个JSON格式的字符串通过HTTP发送给请求方。 |
Response.status() |
设定HTTP状态码 | 如res.status(403).end() ,这时在Postman中可以看到返回的状态码时403。 |
Response.redirect() |
跳转指定的路由 | 如res.redirect('/home') 。 |
4.2 视图文件
express的视图的默认模板引擎文件是以后缀.jade
结尾的(以上在app.js可以设置),它相当于一个HTML的模板文件。在初始项目中默认为我们创建了index.jade
、error.jade
、layout.jade
三个视图文件:
layout.jade文件
doctype html html head title= title link(rel='stylesheet', href='/stylesheets/style.css') body block content
index.jade文件
extends layout block content h1= title p Welcome to #{title}
error.jade文件
extends layout block content h1= message h2= error.status pre #{error.stack}
可以看出,以上layout.jade是index.jade和error.jade公共部分,将前者单独成文件,这样后两者通过extends
模板关键字从前者继承了内容。
4.3 安装项目依赖 并 运行项目
(1)安装项目依赖
你可能注意到我们创建的这个项目中并没有一个名叫“node_modules
”的文件夹,这说明创建express项目时并没有自动地为我们安装依赖,需要我们手动安装。执行以下命令:
yarn install
或者你使用的是npm:
npm install
(2)运行项目(启动Web服务)
安装好项目依赖后就可以启动Web服务了,也就是运行/bin/www
这个文件。打开package.json
发现其内容如下:
{ "name": "myproject", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", "http-errors": "~1.6.3", "jade": "~1.11.0", "morgan": "~1.9.1" }
说明我们在项目的根目录位置可以直接通过start
运行/bin/www
:
yarn start
(或者你使用的是npm):
npm start
现在,让我们打开客户端(浏览器)看看效果吧!express默认使用的是本地的3000端口,在浏览器中输入http://127.0.0.1:3000
或者http://localhost:3000
,可以看到如下页面:
如果需要自定义端口,可以在app.js
文件中添加以下代码指定你想使用的端口:
app.listen(3000); // 默认使用的端口为3000,将数字改成你想要指定的端口号即可
5. 安装使用nodemon
辅助开发
这个是一个小工具,不是必须使用但是比较推荐的。主要是由于我们进行开发express框架的项目调试时,每次修改路由文件都要重新启动项目我们的改动才会生效,这很麻烦。要想自动生效只需要在你的express项目中安装这款工具:
使用yarn安装
yarn global add nodemon
使用npm工具安装
npm install -g nodemon