Application应用对象
Application应用对象
Koa
应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈
的方式组织和执行的。
如何理解这里的类似堆栈的方式呢?直接下面的例子:
const Koa = require('koa')
const app = new Koa()
/**
* logger
* 第一个中间件,记录日志
*/
app.use(async (ctx, next) => {
const middleRes1 = await next()
console.log('middleware1===>', middleRes1)
const rt = ctx.response.get('X-Response-Time')
const { method } = ctx
// 也可以从ctx中获取url对象
console.log(`请求信息:${method} - ${rt}`)
return 'back middleware1 result'
})
/**
* x-response-time
* 第二个中间件,记录响应时间
*/
app.use(async (ctx, next) => {
const start = Date.now()
const middleRes2 = await next()
console.log('middleware2===>', middleRes2)
const ms = Date.now() - start
ctx.set('X-Response-Time', `${ms}ms`)
return 'back middleware2 result'
})
/**
* response
* 最后一个中间件,处理响应
*/
app.use(async (ctx) => {
ctx.body = 'Hello World'
console.log('middleware3===>')
return 'back middleware3 result'
})
app.listen(3000)
当服务运行起来时,会先准备一个栈,等待着被调用,暂且称为调用栈
。 在调用栈里面,三个中间件一次进入栈中, 其中第一个中间件会被压到栈底。
进入栈的顺序(中间件1在栈底):
执行调用的顺序(栈顶的中间件函数先执行):
其中,在中间的处理函数中存在两个参数:
ctx
当前请求的上下文对象next
下一个中间件的执行器
利用next()
可以获取到下一个执行函数的返回结果。例如:在中间件2中可以拿到中间件3的返回结果
到这里应该就能慢慢地理解koa的app对象是如何工作的、调用链路、顺序是怎样的,这也就是来的洋葱圈模型
此刻,想到那首歌:”如果你愿意一层一层地剥开我的心,你会发现、你会讶异....“
设置App属性
在使用koa时,一般是利用Koa模块new一个app对象
const Koa = require('koa')
// 构造函数创建对象
const app = new Koa({ proxy: true })
通过Koa的构造函数源码,可以看到options支持的一些参数:
app.env
默认是 NODE_ENV 或 "development"app.keys
签名的 cookie 密钥数组app.proxy
当真正的代理头字段将被信任时app.subdomainOffset
app.subdomains 偏移量,默认为 2app.proxyIpHeader
代理 ip 消息头, 默认为X-Forwarded-For
app.maxIpsCount
从代理 ip 消息头读取的最大 ips, 默认为 0 (代表无限)
const Koa = require('koa')
// 利用构造函数修改属性
const app1 = new Koa({ proxy: true })
// 定义创建完动态修改属性
const app2 = new Koa()
app2.proxy = true
app.listen
创建HTTP服务,简体端口
创建并返回 HTTP 服务器,将给定的参数传递给 Server#listen(),例如:
const Koa = require('koa')
const app = new Koa()
// 监听3000端口
app.listen(3000)
那么利用app.listen
函数到底做了什么呢?不妨查看下koa
源码,点击listen
即可:
其实可以理解app.listen就是语法糖,调用时利用http.createServer
创建服务端,即等价于:
const http = require('node:http')
const Koa = require('koa')
const app = new Koa()
const server = http.createServer(app.callback())
// 监听端口
server.listen(3000)
当然,如果你需要将程序作为Https
或者多个地址,http
、https
、koa
模块都能很好的支持,例如:
const http = require('node:http')
const server = http.createServer(app.callback())
// 监听端口
server.listen(3000)
const https = require('node:https')
const httpsServer = https.createServer(app.callback())
httpsServer.listen(6000)
运行起来后,可以检查端口是否被监听
app.callback
在上面的启动服务端端口的app.listen
语法糖中可以很清晰的看到app.callback()
,查看源码:
作用是:
对于活跃的
Node.js
的http
服务端,返回请求的处理器回调函数来处理请求,即:返回适用于http.createServer()
方法的回调函数来处理请求
app.use
当你需要使用中间件时,可以使用app.use
方法,传入对应的中间件处理函数,即可调用,查看源码:
use()
方法返回的是this
对象,也就是当use()
方法执行完中间件函数后,返回的是自己的app
原型,熟悉原型链调用的可以知道use()方法支持链式调用
例如:
// 中间件的使用
app.use(middlewareStep1)
app.use(middlewareStep2)
app.listen(4000)
等价于:
// 链式调用
app
.use(middlewareStep1)
.use(middlewareStep2)
.listen(4000)
app.context
app.context
是从其创建 ctx
的原型。可以通过编辑 app.context
为 ctx
添加其他属性,例如:
// 初始化context中的database变量
app.context.database = initDataBase()
// 执行中间件,打印ctx的database变量
app.use((ctx) => {
console.log(ctx.database)
})
可以将ctx
对象理解为当前运行服务的全局上下文对象,支持全局修改、使用
错误处理
按照上面的方式,简单的启动服务端并监听端口,虽然服务可以启动,但这样的服务是稳定性是无法保证的,同时对于服务运行期间出现的错误也是无法及时处理的。
对于Node.js程序,在服务启动时一般是需要对错误进行统一处理、全局捕获的,避免各种错误导致服务进程退出却没有感知
常见的操作如下:
app.on('error', (err) => {
console.error('server error', err)
})
监听error
事件
默认情况下,将所有错误输出到 stderr
,除非 app.silent
为 true。 当 err.status
是 404 或 err.expose
是 true 时默认错误处理程序也不会输出错误。
对于客户端/服务端请求交互出现错误、无法响应时,context
上下文仍然可以在实例中传递,例如:
// 监听error事件
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
})
当应用级错误发生、error
事件监听触发时,可以及时打印出错误信息
更多
Application
对象的可用API还是非常多的,具体可以参考:Application API
一些实践
const Koa = require('koa')
// 利用构造函数修改属性
const app1 = new Koa({ proxy: true })
console.log(app1)
// 定义创建完动态修改属性
const app2 = new Koa()
app2.proxy = true
console.log(app2)