• 作者:老汪软件技巧
  • 发表时间:2024-08-25 17:06
  • 浏览量:

本文是系列文章,其他文章见:

鸿蒙@fw/router框架源码解析(一)-router页面管理

鸿蒙@fw/router框架源码解析(二)-Navigation页面管理

鸿蒙@fw/router框架源码解析(三)-Navigation页面容器封装

鸿蒙@fw/router框架源码解析(四)-路由Hvigor插件实现原理

鸿蒙@fw/router框架源码解析介绍

@fw/router是在HarmonyOS鸿蒙系统中开发应用所使用的开源模块化路由框架。该路由框架基于模块化开发思想设计,支持页面路由和服务路由,支持自定义装饰器自动注册,与系统路由相比使用更便捷,功能更丰富。

具体功能介绍见@fw/router:鸿蒙模块化路由框架,助力开发者实现高效模块化开发!

基于模块化的开发需求,本框架支持以下功能:

详见gitee传送门

代码解析RouterInterceptorManager概述

拦截器是在实际项目中使用路由框架时最为重要的功能,很多工程化、模块化逻辑都可以通过拦截器来实现。

比如,鉴权、埋点、错误统一处理、多协议支持等。

RouterInterceptorManager拦截器管理类主要功能是实现对RouterManager相关方法的拦截处理,并提供拦截器管理功能,支持业务代码添加自定义的拦截器。

RouterInterceptorManager是完全独立的,RouterManager对它并没有依赖。

RouterInterceptorManager的拦截逻辑是通过官方Aspect库通过方法插桩实现的。

那么如果不通过Aspect库是否可以实现呢?也不是不行,就是要RouterManager直接调用RouterInterceptorManager中的同名方法,无法避免两者之间的代码依赖。

RouterInterceptor

拦截器接口定义。

export interface RouterInterceptor {
  open?(request: RouterRequestWrapper): Promise<boolean>
  close?(options?: RouterBackOptionsWrapper): boolean
  onResponse?(request: RouterRequestWrapper, response: RouterResponse): Promise<boolean>
}

open方法代表拦截的路由打开操作,其中request是路由请求参数,注意参数是经过处理的RouterRequestWrapper类型。返回值代表是否拦截,如果返回true,则路由不会继续原处理逻辑。

close方法代码拦截的关闭页面操作,其中options是关闭页面参数,注意参数是经过处理的RouterBackOptionsWrapper类型。返回值逻辑同上。

onResponse方法代表收到响应数据操作,其中request是路由请求对象,response是路由返回对象,返回值逻辑同上。

拦截器管理类和具体的拦截器都需要实现该接口。

RouterInterceptorManager-拦截器管理逻辑

export class RouterInterceptorManager implements RouterInterceptor {
  private interceptors: List<RouterInterceptor> = new List()
  addInterceptor(interceptor: RouterInterceptor) {
    this.interceptors.add(interceptor);
  }
  removeInterceptor(interceptor: RouterInterceptor) {
    this.interceptors.remove(interceptor)
  }
}

拦截器管理类目前简单的用List管理拦截器项目。后续考虑增加优先级等排序逻辑。

RouterInterceptorManager-插桩替换原方法

export class RouterInterceptorManager implements RouterInterceptor {
  replaceRouterManagerFunction() {
    let originFunction: Function = RouterManager.getInstance()._realOpen.bind(RouterManager.getInstance())
    util.Aspect.replace(RouterManager, '_realOpen', false, (instance: RouterManager, request: RouterRequestWrapper) => {
      return new Promise<RouterResponse>((resolve, reject) => {
        this.open(request).then((interrupt) => {
          if (!interrupt) {
            originFunction(request).then(resolve)
          } else {
            if (request) {
              resolve({
                code: RouterResponseError.RequestInterrupt.code,
                msg: RouterResponseError.RequestInterrupt.msg
              })
            }
          }
        })
      })
    })
    let originClose: Function = RouterManager.getInstance().close.bind(RouterManager.getInstance())
    util.Aspect.replace(RouterManager, 'close', false, (instance: RouterManager, options?: RouterBackOptionsWrapper) => {
      let interrupt = this.close(options)
      if (!interrupt) {
        originClose(options)
      }
    })
    let originProcessResult: Function = RouterManager.getInstance().processResponse.bind(RouterManager.getInstance())
    util.Aspect.replace(RouterManager, 'processResponse', false, (instance: RouterManager, resolve: (value: RouterResponse | PromiseLike) => void, request: RouterRequestWrapper, result: RouterResponse) => {
      this.onResponse(request, result).then((interrupt) => {
        if (!interrupt) {
          originProcessResult(resolve, request, result)
        }
      })
    })
  }
}

该方法主要是实现RouterManager中_realOpen、close、processResponse三个方法的替换。

核心代码为:

    let originFunction: Function = RouterManager.getInstance()._realOpen.bind(RouterManager.getInstance())
    util.Aspect.replace(RouterManager, '_realOpen', false, (instance: RouterManager, request: RouterRequestWrapper) => { })

先保存原实现方法,然后通过util.Aspect.replace替换方法实现;

replace方法的定义为static replace(targetClass: Object, methodName: string, isStatic: boolean, instead: Function): void;,其中第四个参数为新方法实现,其参数和原方法的参数相比多了一个instance。

this.open(request).then((interrupt) => {
  if (!interrupt) {
    originFunction(request).then(resolve)
  } else {
    if (request) {
      resolve({
        code: RouterResponseError.RequestInterrupt.code,
        msg: RouterResponseError.RequestInterrupt.msg
      })
    }
  }
})

在新的实现方法中,先调用RouterInterceptorManager的open方法。如果其返回值是false则执行之前保存的原实现方法。如果是true,则直接触发回调,路由的open操作失败。

close和onResponse的操作逻辑类似。不过路由的close方法是同步方法,且没有返回值,因此未处理返回值为true的逻辑。

RouterInterceptorManager-RouterInterceptor接口方法实现

export class RouterInterceptorManager implements RouterInterceptor {
  open(request: RouterRequestWrapper): Promise<boolean> {
    return new Promise<boolean>(async (resolve, reject) => {
      for (const interceptor of this.interceptors) {
        if (interceptor.open && await interceptor.open(request)) {
          resolve(true)
          return
        }
      }
      resolve(false)
    })
  }
  close(options?: RouterBackOptionsWrapper): boolean {
    for (const interceptor of this.interceptors) {
      if (interceptor.close && interceptor.close(options)) {
        return true
      }
    }
    return false
  }
  onResponse(request: RouterRequestWrapper, response: RouterResponse): Promise<boolean> {
    return new Promise<boolean>(async (resolve, reject) => {
      for (const interceptor of this.interceptors) {
        if (interceptor.onResponse && await interceptor.onResponse(request, response)) {
          resolve(true)
          return
        }
      }
      resolve(false)
    })
  }
}

实现RouterInterceptor中定义的三个方法,遍历interceptors列表,并执行相关方法。open和onResponse是异步方法,但目前的设计中调用失败是通过resolve返回错误报文来实现,因此目前是简单的用await来实现遍历调用逻辑,未处理.catch操作。

总结

路由框架中的拦截器属于代码相对简单,但是作用很大的模块;如果你需要对系统路由进行二次封装,那么最好提前把路由拦截逻辑处理好,这样在之后的项目开发中会节省很多成本,大大提升实际的开发效率。