网关辅助类使用说明

本文对网关中用到的相关辅助类,包括拦截器类、MobileRpcHolder,以及网关错误码的使用进行说明。

实现拦截器功能

拦截器只适用于非 HTTP 类型服务。

mobilegw-unify-spi-adapter.jar 实际上是通过 Java 的反射调用业务方法,即OperatioinType 所指定的方法。在方法调用的过程中,业务方可以实现 SPI 包中定义的拦截器,从而实现扩展。

网关的 SPI 包定义了两个拦截器:AbstractMobileServiceInterceptor 抽象类和 MobileServiceInterceptor 接口。

AbstractMobileServiceInterceptor

MobileServiceInterceptor 主要提供了四个方法,分别是 beforeInvokeafterInvoke(分为两种:一种入参为业务返回的 Object;另一种入参为 Object 转成的 JSON string)、throwsInvokegetOrder

43

如上图所示,拦截器主要在以下三种情况下进行拦截:

  • 方法调用前:即 beforeInvoke方法,该方法有返回值。一旦该方法的返回值不为空,那么网关认定拦截成功,将会跳过剩余拦截器的 beforeInvoke 方法,同时跳过调用业务方的方法,直接进入拦截器的 afterInvoke 方法。

  • 方法调用后: 即 afterInvoke 方法。afterInvoke 有两种,一种入参为 Object,即业务方返回的对象,该方法没有返回值,所有的拦截器的该方法都会执行;另一种入参为 Object 转成的 JSON string,该方法可以改变传入的 JSON 数据并返回。一旦返回值不为空,那么网关认定拦截成功,后续的拦截器将被忽略。

  • 方法出现异常:即 throwsInvoke 方法。该方法没有返回值,所有拦截器的该方法都会被执行。在业务方出现异常时会被调用。

MobileServiceInterceptor

MobileServiceInterceptor 继承了框架的 Ordered 接口,因此,业务方实现的拦截器还可以通过实现 getOrder方法指定执行顺序,设置的数值越小,执行的优先级越高;设置的数值越大,执行的优先级越低。

使用示例

  1. 编码自己的拦截器类,继承 AbstractMobileServiceInterceptor 类,或者实现MobileServiceInterceptor 接口。

    public class MyInterceptor implements MobileServiceInterceptor {
    
     /*
     参数说明
         method:即业务方的方法(@OperatioinType 定义的方法)
         args:  一个对象数组,即业务方方法的传入参数,传入参数个数即等于数组大小。
              使用时业务方根据需要进行类型转换。    
         bean: 即业务方的接口实例。
     返回值说明:
         Object:可以在拦截器中返回数据,一旦返回值不为空,则网关认为已被拦截,就不会再调用业务方法
             同时,直接跳过其他拦截器的 beforeInvoke 方法,执行拦截器中的 afterInvoke 方法。       
     */
    
     @Override
     public Object beforeInvoke(Method method, Object[] args, Object target) {
         //Do Something
         return null;
     }
    
     /*
     *参数说明
     *returnValue: 业务方法返回的对象
     * 其它参数同上  
     */
     @Override
     public void afterInvoke(Object returnValue, Method method, Object[] args, Object target) {
         //注意:这里入参是业务方返回的 Object
     }
    
     @Override
     public String afterInvoke(String returnJsonValue, Method method, Object[] args, Object target) {
         //注意:这里入参是由业务方返回的 object,转换而成的 JSON 格式的 string
         //可以返回新的 JSON 数据
         return null;
     }
    
     @Override
     public void throwsInvoke(Throwable t, Method method, Object[] args, Object target) {
     }
    
     @Override
     public int getOrder() {
         //最高级(数值最小)和最低级(数值最大)。
         return 0;
     }
    }
  2. 发布实现的类 MyInterceptor,成为 Bean。

    • Spring Boot:直接在该类加注解 @service

      @service
      public class MyInterceptor implements MobileServiceInterceptor{}
    • Spring:在配置的 xml 文件声明。

      <bean id="myInterceptor" class="com.xxx.xxx.MyInterceptor"/>

MobileRpcHolder 辅助类

MobileRpcHoldermobilegw-unify-spi-adapter.jar 中提供的一个静态辅助类,该类定义了一个请求过程中的相关信息,最主要的定义如下:

Map<String, String> session     保存请求的 session
Map<String, String> header      保存请求的头部相关信息
Map<String, String> context     保存网关调用的上下文信息
String        operationType     保存此次请求的 operationType

在业务方的服务(即 OperationType)被调用之前,SPI 服务会根据网关转发的请求 MobileRpcRequest 去设置 MobileRpcHolder中的信息。在调用业务方服务之后这些信息会被清除。

MobileRpcHolder 的生命周期为整个服务的调用过程,调用后清除。

业务方也可以根据需要去设置这些信息,这些信息会在调用业务的服务过程中一直存在,在调用过程中业务服务可以获取这些信息。具体的设置可以通过拦截器,在方法调用前后动态地修改 MobileRpcHolder 中保存的信息。

以下通过例子说明 MobileRpcHolder 如何使用。

使用示例

这里以修改和获取 session 为例。

  1. 修改 session。 创建拦截器,具体过程参考上面的拦截器示例。下面以在方法调用前拦截为例:

     @Override
     public Object beforeInvoke(Method method, Object[] args, Object target) {
         Map<String, String> session = MobileRpcHolder.getSession();
         session.put("key_test", "value_test");
         MobileRpcHolder.setSession(session);
     }

    这样就能修改 MobileRpcHolder 中的 session 信息。

  2. 获取 session。 业务方在自己定义的服务中可以获取 session 信息。

     @OperationType("com.alipay.account.query")
     public String mock2(String s) {
         Map<String, String> session = MobileRpcHolder.getSession();
     }

    其他信息(如 header、context)的修改和获取方式同上。

     // 获取 header 所有信息
     Map<String,String> headers = MobileRpcHolder.getHeaders();
     // 这里上下文信息指的是请求中的上下文信息
     Map<String,String> context = MobileRpcHolder.getRequestCtx(); 
     // 获取 OperationType
     String opt = MobileRpcHolder.getOperationType();

使用网关错误码

移动网关有自己的一套错误码规范,详见 网关结果码说明

需要注意 BizException 6666,此错误是业务出现异常后,网关会抛出的异常。

如果想要在出现具体错误时,返回其他错误码,可以通过抛出 RpcException(ResultEnum resultCode) 来控制 RPC 层的错误,比如 resultCode=1001,会返回给客户端“没有权限访问”。

代码示例

@Override
public String mock2(String s) throws RpcException {
    try{
        test();
    }catch (Exception e){
        throw new RpcException(IllegalArgument);
    }
    return "11111111";
}

自定义错误码

如果想使用自定义错误码,那么在调用业务方法时不能往外抛异常。

业务方法只要出现异常,就会返回状态码 6666。同时客户端收到该状态码,即认为服务出错,不会去解析业务返回的数据。客户端只有在接收到 1000 状态码时,才会去解析返回的数据。

具体做法是,服务端和客户端约定好具体的错误码,然后在调用业务方法时捕获(catch)所有的异常,将自定义错误码放在返回的数据中。这样即使业务出现异常,网关也会返回 1000 成功。同时客户端去解析返回的数据,提取自定义错误码。