无代码依赖如何实现拦截器逻辑-鸿蒙@fw/router框架源码解析(五)

224 阅读5分钟

本文是系列文章,其他文章见:
鸿蒙@fw/router框架源码解析(一)-router页面管理
鸿蒙@fw/router框架源码解析(二)-Navigation页面管理
鸿蒙@fw/router框架源码解析(三)-Navigation页面容器封装
鸿蒙@fw/router框架源码解析(四)-路由Hvigor插件实现原理
鸿蒙@fw/router框架源码解析(六)-模块化开发如何实现代码解耦

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

介绍

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

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

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

  • 支持页面路由和服务路由;
  • 页面路由支持多种模式(router模式,Navigation模式,混合模式);
  • router模式支持打开非命名路由页面;
  • 页面打开支持多种方式(push/replace),参数传递;关闭页面,返回指定页面,获取返回值,跨页面获取返回值;
  • 支持服务路由,可使用路由url调用公共方法,达到跨技术栈调用以及代码解耦的目的;
  • 支持页面路由/服务路由通过装饰器自动注册;
  • 支持动态导入(在打开路由时才import对应har包),支持自定义动态导入逻辑;
  • 支持添加拦截器(打开路由,关闭路由,获取返回值);
  • Navigation模式下支持自定义Dialog对话框;

详见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<RouterResponse>) => void, request: RouterRequestWrapper, result: RouterResponse) => {
      this.onResponse(request, result).then((interrupt) => {
        if (!interrupt) {
          originProcessResult(resolve, request, result)
        }
      })
    })
  }
}

该方法主要是实现RouterManager_realOpencloseprocessResponse三个方法的替换。

核心代码为:

    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
      })
    }
  }
})

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

closeonResponse的操作逻辑类似。不过路由的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列表,并执行相关方法。openonResponse是异步方法,但目前的设计中调用失败是通过resolve返回错误报文来实现,因此目前是简单的用await来实现遍历调用逻辑,未处理.catch操作。

总结

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