首页
壁纸
统计
友链
Search
1
Nestjs概述-中文
10 阅读
2
ExpressAPI
10 阅读
3
NestJS新手入门核心模块对比表笔记
9 阅读
4
JavaScript企业数据处理实用指南
9 阅读
5
Vue2详细笔记
8 阅读
Nodejs
Vue
Java
Msql
登录
Search
Wasnl
累计撰写
12
篇文章
累计收到
1
条评论
首页
栏目
Nodejs
Vue
Java
Msql
页面
壁纸
统计
友链
搜索到
1
篇与
的结果
2026-02-17
ExpressAPI
Express 4.x APIexpress()express()用来创建一个Express的程序。express()方法是express模块导出的顶层方法。var express = require('express'); var app = express();Methodsexpress.static(root, [options])express.static是Express中唯一的内建中间件。它以server-static模块为基础开发,负责托管 Express 应用内的静态资源。参数root为静态资源的所在的根目录。参数options是可选的,支持以下的属性:属性描述类型默认值dotfiles是否响应点文件。供选择的值有"allow","deny"和"ignore"String"ignore"etag使能或者关闭etagBooleantrueextensions设置文件延期回退Booleantrueindex发送目录索引文件。设置false将不发送。Mixed"index.html"lastModified设置文件在系统中的最后修改时间到Last-Modified头部。可能的取值有false和true。BooleantruemaxAge在Cache-Control头部中设置max-age属性,精度为毫秒(ms)或则一段ms format的字符串Number0redirect当请求的pathname是一个目录的时候,重定向到尾随"/"BooleantruesetHeaders当响应静态文件请求时设置headers的方法Funtion 如果你想获得更多关于使用中间件的细节,你可以查阅Serving static files in Express。Application()app对象一般用来表示Express程序。通过调用Express模块导出的顶层的express()方法来创建它:var express = require('express'); var app = express(); app.get('/', function(req, res) { res.send('hello world!'); }); app.listen(3000);app对象具有以下的方法:路由HTTP请求;具体可以看app.METHOD和app.param这两个例子。配置中间件;具体请看app.route。渲染HTML视图;具体请看app.render。注册模板引擎;具体请看app.engine。它还有一些属性设置,这些属性可以改变程序的行为。获得更多的信息,可以查阅Application settings。Propertiesn.属性;财产;资产;地产app.localsapp.locals对象是一个javascript对象,它的属性就是程序本地的变量。app.locals.title // => 'My App' app.locals.email // => 'me@myapp.com'一旦设定,app.locals的各属性值将贯穿程序的整个生命周期,与其相反的是res.locals,它只在这次请求的生命周期中有效。在程序中,你可以在渲染模板时使用这些本地变量。它们是非常有用的,可以为模板提供一些有用的方法,以及app级别的数据。通过req.app.locals(具体查看req.app),Locals可以在中间件中使用。app.locals.title = 'My App'; app.locals.strftime = require('strftime'); app.locals.email = 'me@myapp.com';app.mountpathapp.mountpath属性是子程序挂载的路径模式。 app.mountpath记录子程序被挂载的路径,方便内部获取挂载位置一个子程序是一个express的实例,其可以被用来作为路由句柄来处理请求。var express = require('express'); var app = express(); // the main app var admin = express(); // the sub app admin.get('/', function(req, res) { console.log(admin.mountpath); // /admin res.send('Admin Homepage'); }); app.use('/admin', admin); // mount the sub app 挂在钩子它和req对象的baseUrl属性比较相似,除了req.baseUrl是匹配的URL路径,而不是匹配的模式。如果一个子程序被挂载在多条路径模式,app.mountpath就是一个关于挂载路径模式项的列表,如下面例子所示。var admin = express(); admin.get('/', function(req, res) { console.log(admin.mountpath); // ['adm*n', '/manager'] res.send('Admin Homepage'); }); var secret = express(); secret.get('/', function(req, res) { console.log(secret.mountpath); // /secr*t res.send('Admin secret'); }); admin.use('secr*t', secret); // load the 'secret' router on '/secr*t', on the 'admin' sub app //在 '/secr*t' 上加载 'secret' 路由,在 'admin' 子应用上 app.use(['/adm*n', '/manager'], admin); // load the 'admin' router on '/adm*n' and '/manager' , on the parent app 在父应用程序上,将“admin”路由加载到“/adm*n”和“/manager”上 Eventsapp.on('mount', callback(parent))当子程序被挂载到父程序时,mount事件被发射。父程序对象作为参数,传递给回调方法。var admin = express(); admin.on('mount', function(parent) { console.log('Admin Mounted'); console.log(parent); // refers to the parent app 指的是母应用程序 }); admin.get('/', function(req, res) { res.send('Admin Homepage'); }); app.use('/admin', admin); Methodsapp.all(path, callback[, callback ...] 全局统一逻辑(如认证、权限校验)场景 1:全局统一逻辑(如认证、权限校验) 如果你的应用需要 “所有请求必须先经过认证”,用app.all('*', ...)可以一次性搞定,无需给每个路由单独加逻辑。场景 2:特定路径前缀的统一处理(如 API 接口白名单)如果只需要对某一类路径(比如/api开头的接口)应用统一逻辑,只需把路径参数从*改为/api/*即可。app.all方法和标准的app.METHOD()`方法相似,除了它匹配所有的HTTP动词。app.all是 Express 中一个特殊的路由方法,它不像app.get(只处理 GET 请求)、app.post`(只处理 POST 请求)那样限制 HTTP 方法,而是对指定路径的 “所有 HTTP 方法” 都生效。对于给一个特殊前缀映射一个全局的逻辑处理,或者无条件匹配,它是很有效的。例如,如果你把下面内容放在所有其他的路由定义的前面,它要求所有从这个点开始的路由需要认证和自动加载一个用户。记住这些回调并不是一定是终点:loadUser可以在完成了一个任务后,调用next()方法来继续匹配随后的路由。app.all('*', requireAuthentication, loadUser);或者这种相等的形式: app.all('*', requireAuthentication); app.all('*', loadUser);另一个例子是全局的白名单方法。这个例子和前面的很像,然而它只是限制以/api开头的路径。app.all('/api/*', requireAuthentication);app.delete(path, callback[, callback ...])它会匹配客户端发送的 DELETE 类型请求,且请求路径与 path 参数一致时,执行后续的 callback 回调函数(通常是处理删除逻辑的中间件或业务函数)。HTTP 协议中,DELETE 方法的语义是 “请求服务器删除指定的资源”(比如删除一篇文章、一个用户、一条订单记录等)。app.delete 正是为了对应这种语义,让开发者能清晰地定义 “删除操作” 的路由。RESTful API 设计:在 RESTful 风格中,DELETE 方法是 “删除资源” 的标准动作,app.delete 是实现这一动作的核心路由方法。任何需要 “删除数据” 的业务场景(如删除评论、删除文件、取消订单等)。路由HTTP DELETE请求到有特殊回调方法的特殊的路径。获取更多的信息,可以查阅routing guide。你可以提供多个回调函数,它们的行为和中间件一样,除了这些回调可以通过调用next('router')来绕过剩余的路由回调。你可以使用这个机制来为一个路由设置一些前提条件,如果不能满足当前路由的处理条件,那么你可以传递控制到随后的路由。app.delete('/', function(req, res) { res.send('DELETE request to homepage'); });app.disable(name)app.enable(name)启用名为 name 的设置项(设为 true)无(支持链式调用)app.enabled(name)检查 name 设置项是否已启用布尔值(true/false)app.disable(name)禁用名为 name 的设置项(设为 false)无(支持链式调用)app.disabled(name)检查 name 设置项是否已禁用布尔值(true/false)app.disable(name):主动禁用名为 name 的设置项(设为 false);隐藏技术栈信息:通过 app.disable('x-powered-by') 移除 X-Powered-By 响应头,避免暴露服务器使用 Express,提高安全性;验证设置状态:用 app.disabled(name) 检查某个设置是否生效(比如判断路由是否启用了大小写敏感);动态调整应用行为:根据环境(开发 / 生产)禁用或启用某些设置(比如生产环境禁用详细错误信息)设置类型为布尔的设置名为name的值为false,此处的name是app settings table中各属性的一个。调用app.set('foo', false)和调用app.disable('foo')是等价的。比如:app.disable('trust proxy'); app.get('trust proxy'); // => falseapp.disabled(name)返回true如果布尔类型的设置值name被禁用为false,此处的name是app settings table中各属性的一个。 app.disabled('trust proxy'); // => true app.enable('trust proxy'); app.disabled('trust proxy'); // => falseapp.enable(name)app.enable(name):主动启用指定的设置项(将其值设为 true);app.enabled(name):查询指定的设置项是否已启用(返回 true 或 false)设置布尔类型的设置值name为true,此处的name是app settings table中各属性的一个。调用app.set('foo', true)和调用app.enable('foo')是等价的。 app.enable('trust proxy'); app.get('trust proxy'); // => trueapp.enabled(name)返回true如果布尔类型的设置值name被启动为true,此处的name是app settings table中各属性的一个。 app.enabled('trust proxy'); // => false app.enable('trust proxy'); app.enabled('trust proxy'); // => trueapp.engine(ext, callback) 注册自定义模板引擎app.engine(ext, callback) 的核心作用是让 Express 支持特定扩展名的模板文件,通过注册自定义或第三方的渲染逻辑,实现动态 HTML 页面的生成。它是 Express 模板渲染系统的 “扩展接口”,让开发者可以灵活选择适合的模板引擎。注册给定引擎的回调,用来渲染处理ext文件。默认情况下,Express需要使用require()来加载基于文件扩展的引擎。例如,如果你尝试渲染一个foo.jade文件,Express在内部调用下面的内容,同时缓存require()结果供随后的调用,来加速性能。 app.engine('jade', require('jade').__express);使用下面的方法对于那些没有提供开箱即用的.__express方法的模板,或者你希望使用不同的模板引擎扩展。比如,使用EJS模板引擎来渲染.html文件: app.engine('html', require('ejs').renderFile);在这个例子中,EJS提供了一个.renderFile方法,这个方法满足了Express规定的签名规则:(path, options, callback),然而记住在内部它只是ejs.__express的一个别名,所以你可以在不做任何事的情况下直接使用.ejs扩展。一些模板引擎没有遵循这种规范,consolidate.js库映射模板引擎以下面的使用方式,所以他们可以无缝的和Express工作。var engines = require('consolidate'); app.engine('haml', engines.haml); app.engine('html', engines.hogan);app.get(name)获得设置名为name的app设置的值,此处的name是app settings table中各属性的一个。如下:app.get('title'); // => undefined app.set('title', 'My Site'); app.get('title'); // => 'My Site'app.get(path, callback [, callback ...])路由HTTP GET请求到有特殊回调的特殊路径。获取更多的信息,可以查阅routing guide。你可以提供多个回调函数,它们的行为和中间件一样,除了这些回调可以通过调用next('router')来绕过剩余的路由回调。你可以使用这个机制来为一个路由设置一些前提条件,如果请求没能满足当前路由的处理条件,那么传递控制到随后的路由。 app.get('/', function(req, res) { res.send('GET request to homepage'); });app.listen(port, [hostname], [backlog], [callback])app.listen() 是 Express 应用的 “启动入口”,通过它可以:让应用监听指定端口,接收客户端请求;简化 HTTP 服务器的创建(替代原生 http.createServer() 的繁琐代码);灵活支持 HTTP/HTTPS 协议,适应不同的部署需求。开发中最常用的形式是 app.listen(端口, 启动回调),比如 app.listen(3000, () => { ... }),这是启动 Express 应用的标准写法。绑定程序监听端口到指定的主机和端口号。这个方法和Node中的http.Server.listen()是一样的。 var express = require('express'); var app = express(); app.listen(3000);通过调用express()返回得到的app实际上是一个JavaScript的Function,被设计用来作为一个回调传递给Node HTTP servers来处理请求。这样,其就可以很简便的基于同一份代码提供http和https版本,所以app没有从这些继承(它只是一个简单的回调)。 var express = require('express'); var https = require('https'); var http = require('http'); http.createServer(app).listen(80); https.createServer(options, app).listen(443);app.listen()方法是下面所示的一个便利的方法(只针对HTTP协议): app.listen = function() { var server = http.createServer(this); return server.listen.apply(server, arguments); };app.METHOD(path, callback [, callback ...])路由一个HTTP请求,METHOD是这个请求的HTTP方法,比如GET,PUT,POST等等,注意是小写的。所以,实际的方法是app.get(),app.post(),app.put()等等。下面有关于方法的完整的表。获取更多信息,请看routing guide。Express支持下面的路由方法,对应与同名的HTTP方法: checkout connect copy delete get head lock merge mkactivity mkcol move m-search notify options patch post propfind proppatch purege put report search subscribe trace unlock unsubscribe 如果使用上述方法时,导致了无效的javascript的变量名,可以使用中括号符号,比如,app['m-search']('/', function ...你可以提供多个回调函数,它们的行为和中间件一样,除了这些回调可以通过调用next('router')来绕过剩余的路由回调。你可以使用这个机制来为一个路由设置一些前提条件,如果请求没有满足当前路由的处理条件,那么传递控制到随后的路由。方法作用范围场景next()当前路由 / 中间件链内执行同路由的下一个回调(正常流程传递)next('router')跳出当前路由,交给下一个路由前置条件不满足时,跳过当前路由剩余逻辑本API文档把使用比较多的HTTP方法app.get(),app.post,app.put(),app.delete()作为一个个单独的项进行说明。然而,其他上述列出的方法以完全相同的方式工作。app.all()是一个特殊的路由方法,它不属于HTTP协议中的规定的方法。它为一个路径加载中间件,其对所有的请求方法都有效。 app.all('/secret', function (req, res) { console.log('Accessing the secret section...'); next(); // pass control to the next handler });app.param([name], callback)app.param` 的回调是一个函数,但更具体地说,它是一种特殊的中间件(参数中间件)。它具备中间件的核心特征(处理请求、传递控制权),同时专门针对路由参数做预处理,是中间件在 “参数处理场景” 下的细分形式。app.param([name], callback) 是 Express 中用于统一处理路由参数的方法,核心作用是:当路由路径中出现指定参数(如 :id、:user)时,自动触发预设的回调函数,实现参数验证、数据加载等逻辑的复用。一、核心作用:给路由参数绑定 “预处理逻辑”路由参数(如 /user/:id 中的 :id)是动态变化的(比如 id 可能是 123、456)。app.param 允许你为这些参数绑定一个 “触发器”:每当路由中包含该参数时,先执行触发器逻辑,再进入路由的实际处理回调。常见用途:验证参数格式(如 id 是否为数字);自动加载关联数据(如通过 id 查询用户信息,并存到 req 对象中,供后续路由使用);统一处理参数错误(如参数无效时直接返回 404)。二、基本用法:单参数绑定语法:app.param(参数名, 回调函数)回调函数的参数固定为:(req, res, next, 参数值, 参数名)req:请求对象;res:响应对象;next:传递控制权的函数;参数值:当前路由中该参数的实际值(如 /user/123 中 :id 的值是 123);参数名:参数的名称(如 id)三、参数为数组:批量绑定多个参数如果需要处理多个参数(如 /user/:id/:page 中的 id 和 page),可以将参数名放在数组中,app.param 会按数组顺序依次触发回调。// 给 :id 和 :page 两个参数绑定预处理逻辑 app.param(['id', 'page'], (req, res, next, value, paramName) => { console.log(`处理参数 ${paramName},值为:${value}`); // 简单验证:id 和 page 必须是数字 if (isNaN(Number(value))) { return res.status(400).send(`参数 ${paramName} 必须是数字`); } next(); // 验证通过,继续下一个参数或路由 }); // 路由:包含 :id 和 :page 参数 app.get('/user/:id/:page', (req, res) => { res.send('参数验证通过'); });处理参数 id,值为:123 处理参数 page,值为:5 访问 /user/abc/5 时:id 参数验证失败,返回 400 错误,page 的回调和路由回调都不会执行。四、关键特性:执行时机与范围只执行一次 per 请求:即使多个路由匹配同一个参数(如两个 /user/:id 路由),app.param 回调在一次请求中只会执行一次。app.param('id', (req, res, next, id) => { console.log('param 回调执行'); // 只打印一次 next(); }); // 两个匹配 /user/:id 的路由 app.get('/user/:id', (req, res, next) => { console.log('路由1'); next(); }); app.get('/user/:id', (req, res) => { console.log('路由2'); res.end(); });访问 /user/123 时,输出:param 回调执行 路由1 路由2 局部性:不被继承:app.param 定义的回调只对当前 app(或路由实例)有效,挂载的子应用(sub-app)不会继承。例如:主应用定义的 app.param('id', ...),不会影响挂载到主应用的子应用中的 :id 参数。给路由参数添加回调触发器,这里的name是参数名或者参数数组,function是回调方法。回调方法的参数按序是请求对象,响应对象,下个中间件,参数值和参数名。如果name是数组,会按照各个参数在数组中被声明的顺序将回调触发器注册下来。还有,对于除了最后一个参数的其他参数,在他们的回调中调用next()来调用下个声明参数的回调。对于最后一个参数,在回调中调用next()将调用位于当前处理路由中的下一个中间件,如果name只是一个string那就和它是一样的(就是说只有一个参数,那么就是最后一个参数,和数组中最后一个参数是一样的)。例如,当:user出现在路由路径中,你可以映射用户加载的逻辑处理来自动提供req.user给这个路由,或者对输入的参数进行验证。 app.param('user', function(req, res, next, id) { User.find(id, function(error, user) { if (err) { next(err); } else if (user){ req.user = user; } else { next(new Error('failed to load user')); } }); });对于Param的回调定义的路由来说,他们是局部的。它们不会被挂载的app或者路由继承。所以,定义在app上的Param回调只有是在app上的路由具有这个路由参数时才起作用。在定义param的路由上,param回调都是第一个被调用的,它们在一个请求-响应循环中都会被调用一次并且只有一次,即使多个路由都匹配,如下面的例子:app.param('id', function(req, res, next, id) { console.log('CALLED ONLY ONCE'); next(); }); app.get('/user/:id', function(req, res, next) { console.log('although this matches'); next(); }); app.get('/user/:id', function(req, res) { console.log('and this mathces too'); res.end(); });当GET /user/42,得到下面的结果: CALLED ONLY ONCE although this matches and this matches tooapp.param(['id', 'page'], function(req, res, next, value) { console.log('CALLED ONLY ONCE with', value); next(); }); app.get('/user/:id/:page', function(req. res, next) { console.log('although this matches'); next(); }); app.get('/user/:id/:page', function (req, res, next) { console.log('and this matches too'); res.end(); }); 当执行GET /user/42/3,结果如下:CALLED ONLY ONCE with 42 CALLED ONLY ONCE with 3 although this matches and this mathes too下面章节描述的app.param(callback)在v4.11.0之后被弃用。通过只传递一个回调参数给app.param(name, callback)方法,app.param(naem, callback)方法的行为将被完全改变。这个回调参数是关于app.param(name, callback)该具有怎样的行为的一个自定义方法,这个方法必须接受两个参数并且返回一个中间件。这个回调的第一个参数就是需要捕获的url的参数名,第二个参数可以是任一的JavaScript对象,其可能在实现返回一个中间件时被使用。这个回调方法返回的中间件决定了当URL中包含这个参数时所采取的行为。在下面的例子中,app.param(name, callback)参数签名被修改成了app.param(name, accessId)。替换接受一个参数名和回调,app.param()现在接受一个参数名和一个数字。 var express = require('express'); var app = express(); app.param(function(param, option){ return function(req, res, next, val) { if (val == option) { next(); } else { res.sendStatus(403); } } }); app.param('id', 1337); app.get('/user/:id', function(req, res) { res.send('Ok'); }); app.listen(3000, function() { console.log('Ready'); }); 在这个例子中,app.param(name, callback)参数签名保持和原来一样,但是替换成了一个中间件,定义了一个自定义的数据类型检测方法来检测user id的类型正确性。 app.param(function(param, validator) { return function(req, res, next, val) { if (validator(val)) { next(); } else { res.sendStatus(403); } } }); app.param('id', function(candidate) { return !isNaN(parseFloat(candidate)) && isFinite(candidate); });在使用正则表达式来,不要使用.。例如,你不能使用/user-.+/来捕获user-gami,用使用[\\s\\S]或者[\\w\\>W]来代替(正如/user-[\\s\\S]+/)。//captures '1-a_6' but not '543-azser-sder' router.get('/[0-9]+-[[\\w]]*', function); //captures '1-a_6' and '543-az(ser"-sder' but not '5-a s'router.get('/[0-9]+-[[\S]]*', function); //captures all (equivalent to '.*')router.get('[[\s\S]]*', function);app.param 是处理路由参数的 “利器”,通过它可以: 集中验证参数格式,避免在每个路由中重复编写; 自动加载关联数据(如用户、商品),简化后续路由逻辑; 统一处理参数错误,减少冗余代码。app.path()通过这个方法可以得到app典型的路径,其是一个string。 var app = express() , blog = express() , blogAdmin = express(); app.use('/blog', blog); app.use('/admin', blogAdmin); console.log(app.path()); // '' console.log(blog.path()); // '/blog' console.log(blogAdmin.path()); // '/admin'如果app挂载很复杂下,那么这个方法的行为也会很复杂:一种更好用的方式是使用req.baseUrl来获得这个app的典型路径。app.post(path, callback, [callback ...])路由HTTP POST请求到有特殊回调的特殊路径。获取更多的信息,可以查阅routing guide。你可以提供多个回调函数,它们的行为和中间件一样,除了这些回调可以通过调用next('router')来绕过剩余的路由回调。你可以使用这个机制来为一个路由设置一些前提条件,如果请求没能满足当前路由的处理条件,那么传递控制到随后的路由。 app.post('/', function(req, res) { res.send('POST request to homepage') });app.put(path, callback, [callback ...])路由HTTP PUT请求到有特殊回调的特殊路径。获取更多的信息,可以查阅routing guide。你可以提供多个回调函数,它们的行为和中间件一样,除了这些回调可以通过调用next('router')来绕过剩余的路由回调。你可以使用这个机制来为一个路由设置一些前提条件,如果请求没能满足当前路由的处理条件,那么传递控制到随后的路由。 app.put('/', function(req, res) { res.send('PUT request to homepage'); });app.render(view, [locals], callback)一、核心作用app.render 的本质是 “模板渲染引擎的调用入口”,它会:找到指定名称的模板文件(如 email 对应 views/email.ejs 或 views/email.hbs,取决于你配置的模板引擎);将 locals 中的数据(如 {name: 'Tobi'})注入模板,生成最终的 HTML 字符串;通过 callback 回调函数返回结果(成功时返回 HTML,失败时返回错误信息)。关键区别:它和 res.render() 功能相似,但 res.render() 会自动将渲染后的 HTML 作为响应发送给客户端,而 app.render() 只生成 HTML,不发送 —— 你可以用这个 HTML 做其他事情(比如发邮件、存缓存)。二、参数详解语法:app.render(view, [locals], callback)三个参数的作用如下:参数类型说明view字符串必选,模板文件的名称(无需写路径和扩展名,Express 会按 view engine 配置自动查找)。locals对象可选,注入模板的本地数据(如 {name: '张三'},模板中可通过 {{name}} 调用)。callback函数必选,渲染完成后的回调,格式为 (err, html) => {}:- err:渲染失败时的错误对象;- html:渲染成功后的 HTML 字符串。三、与 res.render() 的关键区别很多人会混淆这两个方法,核心差异在于 “是否自动发送响应” 和 “使用场景”,具体对比如下:对比维度app.render()res.render()输出方式不发送响应,通过 callback 返回 HTML自动将 HTML 作为响应发送给客户端核心用途生成 HTML 后需二次处理(如发邮件)直接响应页面给客户端(如渲染网页)调用依赖可在任何地方调用(无需 req/res)只能在路由 / 中间件中调用(依赖 res 对象)// 1. res.render():直接渲染并发送页面给客户端 app.get('/index', (req, res) => { res.render('index', { title: '首页' }); // 渲染 index 模板,直接响应 HTML }); // 2. app.render():生成 HTML 后自行处理(比如打印或发邮件) app.get('/send-email', (req, res) => { // 生成 email 模板的 HTML app.render('email', { name: 'Tobi' }, (err, html) => { if (err) return res.status(500).send('渲染失败'); // 这里可以用 HTML 做其他事(比如调用邮件服务发送) console.log('邮件内容 HTML:', html); res.send('邮件 HTML 已生成,准备发送'); // 手动给客户端响应 }); });四、典型使用场景app.render() 适合需要 “先获取 HTML,再做额外操作” 的场景,常见例子:1. 生成邮件内容很多邮件需要 HTML 格式(如注册欢迎邮件、营销邮件),此时用 app.render() 生成 HTML 后,传给邮件发送库(如 nodemailer),而不是响应给客户端。const nodemailer = require('nodemailer'); // 邮件发送库 // 生成邮件 HTML 并发送 app.post('/send-welcome-email', (req, res) => { const userEmail = req.body.email; // 1. 用 app.render() 生成邮件模板的 HTML app.render('welcome-email', { username: '张三' }, (err, emailHtml) => { if (err) return res.status(500).send('邮件模板渲染失败'); // 2. 用 nodemailer 发送 HTML 邮件 const transporter = nodemailer.createTransport({ /* 邮件配置 */ }); transporter.sendMail({ to: userEmail, subject: '欢迎注册', html: emailHtml // 用渲染好的 HTML 作为邮件内容 }, (err) => { if (err) return res.send('邮件发送失败'); res.send('欢迎邮件已发送'); }); }); });2. 预渲染 HTML 并缓存对于访问频繁的页面(如首页),可以提前用 app.render() 生成 HTML,存入缓存(如 Redis),后续请求直接从缓存取 HTML 发送,减少重复渲染的性能消耗。const redis = require('redis'); // Redis 缓存库 const client = redis.createClient(); // 预渲染首页 HTML 并存入缓存 app.get('/preload-index', (req, res) => { app.render('index', { title: '首页' }, (err, html) => { if (err) return res.status(500).send('预渲染失败'); // 存入 Redis,过期时间 1 小时 client.set('index-html', html, 'EX', 3600); res.send('首页 HTML 已预渲染并缓存'); }); }); // 从缓存获取 HTML 响应 app.get('/', (req, res) => { client.get('index-html', (err, cachedHtml) => { if (cachedHtml) { return res.send(cachedHtml); // 直接用缓存的 HTML } // 缓存不存在时,用 res.render() 实时渲染 res.render('index', { title: '首页' }); }); });五、注意点:视图缓存开发环境(process.env.NODE_ENV === 'development'):视图缓存默认关闭,每次 app.render() 都会重新读取模板文件,方便调试。生产环境(process.env.NODE_ENV === 'production'):视图缓存默认开启,模板文件会被缓存到内存,重复渲染时无需重新读取文件,提升性能。若需在开发环境开启缓存,可手动设置:app.set('view cache', true)。总结app.render() 是 Express 提供的 “HTML 生成工具”,核心价值在于脱离 “渲染即响应” 的绑定,让你能灵活处理渲染后的 HTML(发邮件、存缓存等)。它和 res.render() 的关系可以理解为:res.render() = app.render() + 自动发送响应。通过callback回调返回一个view渲染之后得到的HTML文本。它可以接受一个可选的参数,可选参数包含了这个view需要用到的本地数据。这个方法类似于res.render(),除了它不能把渲染得到的HTML文本发送给客户端。将app.render()当作是可以生成渲染视图字符串的工具方法。在res.render()内部,就是使用的app.render()来渲染视图。如果使能了视图缓存,那么本地变量缓存就会保留。如果你想在开发的过程中缓存视图,设置它为true。在生产环境中,视图缓存默认是打开的。 app.render('email', function(err, html) { // ... }); app.render('email', {name:'Tobi'}, function(err, html) { // ... });app.route(path)返回一个单例模式的路由的实例,之后你可以在其上施加各种HTTP动作的中间件。使用app.route()来避免重复路由名字(例如错字错误)--说的意思应该是使用app.router()这个单例方法来避免同一个路径多个路由实例。var app = express(); app.route('/events') .all(function(req, res, next) { // runs for all HTTP verbs first //首先运行所有 HTTP 动词的处理程序// 可以把它看作特定路由的中间件! // think of it as route specific middleware! }) .get(function(req, res, next) { res.json(...); }) .post(function(req, res, next) { // maybe add a new event... }) app.set(name, value)给name设置项赋value值,name是app settings table中属性的一项。对于一个类型是布尔型的属性调用app.set('foo', ture)等价于调用app.enable('foo')。同样的,调用app.set('foo', false)等价于调用app.disable('foo')。可以使用app.get()来取得设置的值: app.set('title', 'My Site'); app.get('title'); // 'My Site'Application Settings如果name是程序设置之一,它将影响到程序的行为。下边列出了程序中的设置。这些是 Express.js 框架的应用配置项(Application Settings),核心作用是统一控制 Express 应用的核心行为,比如路由匹配规则、数据格式处理、缓存策略、环境适配等,让开发者能根据项目需求定制框架的运行方式,而不用修改框架源码。1. 路由匹配规则类:控制 URL 如何被识别这类配置决定了 Express 如何匹配客户端请求的 URL,是路由设计的基础。case sensitive routing(区分大小写路由)控制 URL 是否区分大小写。比如启用后,/Foo和/foo会被视为两个不同的路由;默认不启用,两者处理逻辑一致。strict routing(严格路由)控制 URL 末尾的斜杠是否影响匹配。比如启用后,/foo和/foo/是两个不同的路由;默认不启用,两者处理逻辑一致。subdomain offset(子域名偏移量)处理多子域名场景时,用于 “忽略” 域名中前面的几个部分,准确识别子域名。比如默认值为 2 时,a.b.example.com会被识别为子域名a.b(忽略最后两个部分example.com)。2. 数据处理类:控制请求 / 响应的数据格式这类配置负责处理客户端与服务器之间的数据交互格式,比如 JSON、URL 参数等。jsonp callback name(JSONP 回调名)定义 JSONP 请求的默认回调函数名。默认是?callback=,比如请求/?callback=handleData时,响应会包裹在handleData(...)中。json replacer /json spaces(JSON 处理)json replacer:自定义 JSON 序列化规则的回调函数,比如过滤响应中不需要的字段。json spaces:设置后会返回 “格式化缩进” 的 JSON 响应(比如值为 2 时缩进 2 个空格),默认不启用(返回压缩的 JSON),开发时启用方便调试。query parser(URL 参数解析器)控制如何解析 URL 中的查询参数(比如?name=foo&age=18)。默认用extended模式(支持复杂参数,如数组),也可禁用(false)或用原生simple模式。3. 缓存优化类:提升请求响应效率这类配置通过缓存机制减少重复计算或数据传输,是性能优化的关键。etag(ETag 响应头)控制是否生成 HTTP 的 ETag 头,用于客户端缓存验证。默认值为weak(弱 ETag,基于资源语义匹配),可根据需求改为强 ETag 或禁用,避免重复传输未修改的资源(客户端会返回 304 状态码)。view cache(视图模板缓存)控制是否缓存 “编译后的模板文件”(比如 EJS、Pug 模板)。生产环境默认开启(减少重复编译,提升速度),开发环境建议关闭(修改模板后无需重启服务即可生效)。4. 环境与安全类:适配运行环境 & 降低安全风险这类配置用于适配不同运行环境(开发 / 生产),并规避基础安全问题。env(环境模式)定义 Express 运行的环境,默认优先读取系统环境变量NODE_ENV,没有则为development(开发环境)。框架会根据环境自动调整行为,比如生产环境会关闭错误详情提示。trust proxy(信任反向代理)当 Express 部署在反向代理(如 Nginx、Apache)后面时,需启用此配置,才能通过X-Forwarded-*头获取客户端真实 IP;默认禁用,不启用会导致req.ip拿到的是代理服务器的 IP。x-powered-by(X-Powered-By 头)控制是否在响应头中添加X-Powered-By: Express,默认启用。生产环境建议禁用,避免暴露服务器使用的框架,降低被针对性攻击的风险。5. 视图模板类:控制模板文件的查找与渲染这类配置仅在使用 Express 模板引擎(如 EJS、Pug)时生效,负责管理模板文件的路径和默认引擎。views(模板文件目录)指定模板文件存放的目录(或目录数组,数组会按顺序查找模板)。默认路径是项目根目录下的views文件夹(如process.cwd() + '/views')。view engine(默认模板引擎)指定默认的模板引擎,比如设置为ejs后,渲染模板时无需写后缀(如res.render('index')会自动查index.ejs)。属性类型值默认case sensitive routingBoolean启用区分大小写。不启用。对/Foo和/foo处理是一样。envString环境模型。process.env.NODE_ENV(NODE_ENV环境变量)或者"development"etagVaried设置ETag响应头。可取的值,可以查阅etag options table。更多关于HTTP ETag header。weakjsonp callback nameString指定默认JSONP回调的名称。?callback=json replacerStringJSON替代品回调nulljson spacesNumber当设置了这个值后,发送缩进空格美化过的JSON字符串。Disabledquery parserVaried设置值为false来禁用query parser,或者设置simple,extended,也可以自己实现query string解析函数。simple基于Node原生的query解析,querystring。"extend"strict routingBoolean启用严格的路由。不启用。对/foo和/foo/的路由处理是一样。subdomain offsetNumber用来删除访问子域的主机点分部分的个数2trust proxyVaried指示app在一个反向代理的后面,使用x-Forwarded-*来确定连接和客户端的IP地址。注意:X-Forwarded-*头部很容易被欺骗,所有检测客户端的IP地址是靠不住的。trust proxy默认不启用。当启用时,Express尝试通过前端代理或者一系列代理来获取已连接的客户端IP地址。req.ips属性包含了已连接客户端IP地址的一个数组。为了启动它,需要设置在下面trust proxy options table中定义的值。trust proxy的设置实现使用了proxy-addr包。如果想获得更多的信息,可以查阅它的文档DisableviewsString or Arrayview所在的目录或者目录数组。如果是一个数组,将按在数组中的顺序来查找view。process.cwd() + '/views'view cacheBoolean启用视图模板编译缓存。在生成环境默认开启。view engineString省略时,默认的引擎被扩展使用。 x-powered-byBoolean启用X-Powered-By:ExpressHTTP头部trueOptions for trust proxy settings查阅Express behind proxies来获取更多信息。 TypeValue Boolean 如果为true,客户端的IP地址作为X-Forwarded-*头部的最左边的条目。如果为false,可以理解为app直接与英特网直连,客户端的IP地址衍生自req.connection.remoteAddress。false是默认设置。 IP addresses 一个IP地址,子网,或者一组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') // specify a single subnet app.set('trust proxy', 'loopback, 123.123.123.123') // specify a subnet and an address app.set('trust proxy', 'loopback, linklocal, uniquelocal') // specify multiple subnets as CSV app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) // specify multiple subnets as an array 当指定IP地址之后, 这个IP地址或子网会被设置了这个IP地址或子网的`app`排除在外, 最靠近程序服务的没有委托的地址将被看做客户端IP地址。 Number 信任从反向代理到app中间小于等于n跳的连接为客户端。 Function 客户自定义委托代理信任机制。如果你使用这个,请确保你自己知道你在干什么。 app.set('trust proxy', function (ip) { if (ip === '127.0.0.1' || ip === '123.123.123.123') return true; // trusted IPs else return false; }) Options for etag settingsETag功能的实现使用了etag包。如果你需要获得更多的信息,你可以查阅它的文档。 TypeValue 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 }) app.use([path,], function [, function...])挂载中间件方法到路径上。如果路径未指定,那么默认为"/"。一个路由将匹配任何路径如果这个路径以这个路由设置路径后紧跟着"/"。比如:app.use('/appale', ...)将匹配"/apple","/apple/images","/apple/images/news"等。中间件中的req.originalUrl是req.baseUrl和req.path的组合,如下面的例子所示。app.use('/admin', function(req, res, next) { // GET 'http://www.example.com/admin/new' console.log(req.originalUrl); // '/admin/new' console.log(req.baseUrl); // '/admin' console.log(req.path);// '/new' });在一个路径上挂载一个中间件之后,每当请求的路径的前缀部分匹配了这个路由路径,那么这个中间件就会被执行。由于默认的路径为/,中间件挂载没有指定路径,那么对于每个请求,这个中间件都会被执行。 // this middleware will be executed for every request to the app. //这个中间件将会对应用的每个请求执行。 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'); }); // this middleware will never reach this route app.use('/', function(req, res) { res.send('Welcome'); }); 路径可以是代表路径的一串字符,一个路径模式,一个匹配路径的正则表达式,或者他们的一组集合。will match paths starting with /abcd 将匹配以 /abcd 开头的路径 Type Example Path // will match paths starting with /abcd app.use('/abcd', function (req, res, next) { next(); }) Path Pattern // will match paths starting with /abcd and /abd app.use('/abc?d', function (req, res, next) { next(); }) // will match paths starting with /abcd, /abbcd, /abbbbbcd and so on app.use('/ab+cd', function (req, res, next) { next(); }) // will match paths starting with /abcd, /abxcd, /abFOOcd, /abbArcd and so on app.use('/ab*cd', function (req, res, next) { next(); }) // will match paths starting with /ad and /abcd app.use('/a(bc)?d', function (req, res, next) { next(); }) Regular Expression // will match paths starting with /abc and /xyz app.use(//abc|/xyz/, function (req, res, next) { next(); }) Array // will match paths starting with /abcd, /xyza, /lmn, and /pqr app.use(['/abcd', '/xyza', //lmn|/pqr/], function (req, res, next) { next(); }) 方法可以是一个中间件方法,一系列中间件方法,一组中间件方法或者他们的集合。由于router和app实现了中间件接口,你可以像使用其他任一中间件方法那样使用它们。 Usage Example 单个中间件 // 局部中间件函数 app.use(function (req, res, next) { next(); }) // 挂载 Router const router = express.Router(); router.get('/', function (req, res, next) { next(); }) app.use(router); // 挂载子应用 const subApp = express()
2026年02月17日
10 阅读
0 评论
0 点赞