用过 koa 的人都知道,洋葱模型是一种很巧妙的中间件模式,利用嵌套函数,它将响应和请求处理优雅封装在一个中间件中
Koa 中间件示例:
app.use(async (ctx, next) => {
await next();
});
当我们处理请求时,是针对每一个请求做拆分,最小的关注点应是这个请求整体 (Request & Response),而不是将这个请求的 Request 和 Response 分别拆分在两个独立的逻辑中
最好我们能直接通过代码 syntax 层面直观的看出请求和响应的因果关系,而不是通过 function 调用顺序
比如使用 Koa 和 Axios 实现同样一个请求计时逻辑:
Koa
app.use(async (ctx, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
console.log(ms)
})
Axios
let start = 0
axios.interceptors.request.use(function(config){
start = Date.now()
})
axios.interceptors.response.use(function(response){
const ms = Date.now() - start
console.log(ms)
})
在 axios 代码中,我们只是实现计时这一单一逻辑,却被强行拆分到两个 Hooks 中,单从 syntax 层面看不出他们有任何关系,只能创建一个外部变量来把它们耦合在一起
可以看出,洋葱 Middleware 和常规的 Hooks 比起来 ,前者看起来代码更简洁,逻辑也更内聚
洋葱模型在处理 Before / After 这种逻辑中有天然的优势,比如 vue-router、redux 也是使用这种方式,既然洋葱模型可以用在后端请求,那当然也可以用于客户端请求
所以,一个基于洋葱模型的 HTTP 客户端呼之欲出,它长这样:
Resreq
import Resreq from 'resreq'
const resreq = new Resreq({
baseUrl: 'https://example.com'
})
// Intercepting responses and requests using middleware
resreq.use((next) => async (req) => {
try {
console.log(req) // Request can be changed here
const res = await next(req)
console.log(res) // Response can be changed here
return res
} catch (error) {
console.log(error) // Catch errors here
throw error
}
})
const res = await resreq.get('/api', {
params: { foo: 'bar' }
})
console.log(res)
源码:GitHub - molvqingtai/resreq: Fetch-based onion model http client.