如何在js中拦截回调函数,即根据需要修改模块函数

问题总结(Problem Summary)

You are using a npm module with supported functions that have callbacks as parameters. You want that every time you handle a callback, you add some tasks before the actual callback, such as logging “callback xyz is about to run”. In this article, I will show you how to do that by modifying the prototype of the module’s API functions. This concept is known as intercepting. It’s pretty similar to the concept “middleware” in HTTP server request handling.

您正在使用npm模块,其支持的功能将回调作为参数。 您希望每次处理回调时,都在实际回调之前添加一些任务,例如记录“回调xyz将要运行”。 在本文中,我将向您展示如何通过修改模块的API函数的原型来做到这一点。 这个概念称为拦截。 它与HTTP服务器请求处理中的“中间件”概念非常相似。

示例场景 (Sample scenario)

My project was using the npm module “request” and I wanted to intercept the response callback for every request called from this module. Unfortunately, the module doesn’t support this feature. I couldn’t choose another module that supports it (such as “axios”) for historical reasons. To understand more about the problem, please look at this code:

我的项目正在使用npm模块“ request”,我想拦截从该模块调用的每个请求的响应回调。 不幸的是,该模块不支持此功能。 由于历史原因,我无法选择支持它的另一个模块(例如“ axios”)。 要了解有关该问题的更多信息,请查看以下代码:

const request = require('request');
const myMiddleware = require('./middleware');request('https://google.com', (err, res, body) => {
myMiddleware(); // I need to intercept this function to every response handling, i.e. callback
if (err) {
console.log(err);
return;
}
// do something with the response
});

For every function call like this, I want to integrate a middleware to it so it can do some tasks like logging the status code, verify the response data. I want that the myMiddleware() is automatically called in every response handling (callback), instead of repeating this function call. Therefore, I had to “cheat” the API functions of the module so it can behave differently as I wanted.

对于这样的每个函数调用,我都希望将一个中间件集成到该中间件,以便它可以执行某些任务,例如记录状态代码,验证响应数据。 我希望在每个响应处理(回调)中自动调用myMiddleware(),而不是重复此函数调用。 因此,我不得不“欺骗”模块的API函数,以使其表现出与所需不同的效果。

(Solution)

Please note that this solution is very tricky, it’s is to modifying the original module functions. So use it at your own risk.

请注意,此解决方案非常棘手,它是修改原始模块功能。 因此,使用它需要您自担风险。

Before jumping in the solution, let’s me remind you how a callback by looking at this code:

在进入解决方案之前,让我通过查看以下代码来提醒您如何进行回调:

const doSomething = (param1, callback) => {
// the original function handling that lead to some results
const result = param1;
callback(result);
};doSomething(1, (input) => {
console.log('callback called, input is', input);
});// 'callback called, input is 1' is logged out

So I defined a function doSomething();, it does some specific tasks and when everything done, it passes result(s) to the callback so the following tasks can be different in different function calls doSomething();. As you can see the callback is always called at the end of the original function handling, when all the original tasks are done.

因此,我定义了一个函数doSomething();,它执行一些特定的任务,并且在完成所有操作后,将结果传递给回调,以便在不同的函数调用doSomething();中以下任务可以有所不同 如您所见,当所有原始任务完成后,回调总是在原始函数处理结束时调用。

My solution is to add my custom tasks before the callback call. Have a look at this code:

我的解决方案是在回调调用之前添加我的自定义任务。 看一下这段代码:

const request = requires('request')
const myMiddleware = require('./middleware');// intercept the callback
const _original = request
request = (a1, a2, a3) => {
const _callback = a2
a2 = (err, res, body) => {
myMiddleware() // <--------------
_callback(err, res, body)
}
_original(a1, a2, a3)
}// keep all the properties/prototypes of the module export
Object.keys(_original).forEach(key => {
request[key] = _original[key]
})request('https://google.com', (err, res, body) => {
if (err) {
console.log(err);
return;
}
// do something with the response
});

The original request(); treats the second parameter (a2) as the callback. The callback function has three parameters (err, res, body) input by the original tasks. I assign the original callback as _callback then replace the actual callback with myMiddleware(), then _callback(), so the original callback is still called but following to my interceptor (myMiddleware()). After that, I replace the original module function (_original) with the one that has the customized callback then use it instead. So that’s how you intercept a callback function using characteristics of JaveScript!

原始request(); 将第二个参数(a2)视为回调。 回调函数具有由原始任务输入的三个参数(err,res,body) 。 我将原始回调分配为_callback,然后用myMiddleware()_callback()替换实际的回调因此仍调用原始回调,但随后是我的拦截器( myMiddleware() ) 之后,我将原始模块函数(_original)替换为具有自定义回调的函数,然后改用它。 这样便可以利用JaveScript的特性来拦截回调函数!

For this specific case, the variable const request = require(‘request’) has other properties/prototypes such as request.get(), request.post(). So you have to reassign the customized request’s properties/prototypes to the original ones, using Object.keys.forEach().

对于这种特定情况,变量const request = require('request')具有其他属性/原型,例如request.get(),request.post() 因此,您必须使用Object.keys.forEach()将自定义请求的属性/原型重新分配给原始属性/原型。

额外 (Extra)

If you want to apply this to your entire project, meaning that every time you require the module ‘request’ in a certain file, the interceptor is added, you can use the module npm ‘intercept-require’ like this:

如果要将其应用于整个项目,这意味着每次在某个文件中需要模块“请求”都会添加拦截器,您可以使用模块npm “ intercept-require”,如下所示:

const moduleInterceptor = require('intercept-require');moduleInterceptor((module, info) => {
if (info.moduleId !== 'request') { return module; }
// intercept the callback return module;
})const request = require('request');
// using request

闭幕 (Closing)

Hope this article can help you to understand more about JavaScript and save some time to handle or workaround some tricky issues.

希望本文可以帮助您了解有关JavaScript的更多信息,并节省一些时间来处理或解决一些棘手的问题。

翻译自: https://medium.com/@vukhoa32/how-to-intercept-a-callback-function-in-js-i-e-modifying-module-function-for-your-need-786643812d5c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Uniapp ,可以使用路由守卫来实现登录拦截。路由守卫是用来在路由导航过程进行拦截和控制的机制,可以在路由跳转之前或之后执行一些操作。 要实现登录拦截,可以在全局的 main.js 文件配置路由守卫。以下是一个简单的示例: ```javascript // main.js // 导入uni-app的路由模块 import Vue from 'vue' import App from './App' import router from './router' // 在路由跳转之前执行的操作 router.beforeEach((to, from, next) => { // 判断用户是否已登录 const isLogged = false; // 根据你的业务逻辑判断用户是否已登录 if (to.meta.requireAuth && !isLogged) { // 如果目标路由需要登录,并且用户未登录,则跳转到登录页 next('/login'); } else { // 否则,放行路由 next(); } }); // 创建Vue实例 new Vue({ el: '#app', router, components: { App }, template: '<App/>' }) ``` 在上述代码,我们使用 `router.beforeEach` 方法来定义一个全局的路由守卫。在每次路由跳转之前,会先执行这个函数。在函数,我们通过判断用户是否已登录来决定是否进行拦截。 如果目标路由需要登录(通过 `to.meta.requireAuth` 来判断),且用户未登录,则通过 `next('/login')` 跳转到登录页。否则,直接用 `next()` 放行路由。 需要注意的是,以上只是一个简单的示例,你可以根据你的业务需求来进行相应的整和扩展。另外,还可以在具体的路由配置,通过设置 `meta` 字段来定义哪些路由需要登录验证。例如: ```javascript // router.js import Vue from 'vue' import Router from 'vue-router' import Home from '@/views/Home' import Login from '@/views/Login' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Home', component: Home, meta: { requireAuth: true // 需要登录验证 } }, { path: '/login', name: 'Login', component: Login } ] }) ``` 在上述代码,我们在路由配置为 Home 路由添加了 `meta.requireAuth` 字段,并设置为 `true`,表示需要登录验证。这样在全局的路由守卫,就可以根据这个字段来判断是否进行拦截。 希望这个答对你有帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值