微信后台----公众号通讯封装----WxMpService

execute用来封装发送http请求。并不是并发线程的控制器。

1.接口定义 WxMpService

/**
 * 初始化http请求对象.
 */
void initHttp();

/**
 * @return RequestHttp对象
 */
RequestHttp getRequestHttp();

/**
 * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求.
 */
String get(String url, String queryParam) throws WxErrorException;

/**
 * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
 */
String post(String url, String postData) throws WxErrorException;

 /**
   * Service没有实现某个API的时候,可以用这个,
   * 比{@link #get}和{@link #post}方法更灵活,可以自己构造RequestExecutor用来处理不同的参数和不同的返回类型。
   * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
   */
   T execute(RequestExecutor executor, String uri, E data) throws WxErrorException;

实现类 --- BaseWxMpServiceImpl

  @Override
  public String get(String url, String queryParam) throws WxErrorException {
    return execute(SimpleGetRequestExecutor.create(this), url, queryParam);
  }

  @Override
  public String post(String url, String postData) throws WxErrorException {
    return execute(SimplePostRequestExecutor.create(this), url, postData);
  }

 

2.具体实现:BaseWxMpServiceImpl---重试控制:-1 系统繁忙, 1000ms后重试

 private int maxRetryTimes = 5;             重试次数
 private int retrySleepMillis = 1000;       重试时间
  • 像微信发送消息,如果出现异常,最多重试5次。
  • 重试:do  ...  while 
/**
   * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求.
   * <<      :     左移运算符,num << 1,相当于num乘以2
     >>      :     右移运算符,num >> 1,相当于num除以2

   */
  @Override
  public  T execute(RequestExecutor executor, String uri, E data) throws WxErrorException {
    int retryTimes = 0;
    do {
      try {
        return this.executeInternal(executor, uri, data);
      } catch (WxErrorException e) {
        if (retryTimes + 1 > this.maxRetryTimes) {
          this.log.warn("重试达到最大次数【{}】", maxRetryTimes);
          //最后一次重试失败后,直接抛出异常,不再等待
          throw new RuntimeException("微信服务端异常,超出重试次数");
        }

        WxError error = e.getError();
        // -1 系统繁忙, 1000ms后重试
        if (error.getErrorCode() == -1) {
          int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
          try {
            this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
            Thread.sleep(sleepMillis);
          } catch (InterruptedException e1) {
            throw new RuntimeException(e1);
          }
        } else {
          throw e;
        }
      }
    } while (retryTimes++ < this.maxRetryTimes);

    this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
    throw new RuntimeException("微信服务端异常,超出重试次数");
  }

3.具体实现---发送

  • 屏蔽日志中敏感信息
  • 请求uri参数中不允许有access_token
  • 获取access_token
  • uri参数后拼接access_token
  • 调用请求执行器
  • 尝试刷新access_token
  public  T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
    //1.屏蔽日志中敏感信息
    E dataForLog = DataUtils.handleDataWithSecret(data);
    //2.请求uri参数中不允许有access_token
    if (uri.contains("access_token=")) {
      throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
    }
    //3.获取access_token
    String accessToken = getAccessToken(false);
    //4.uri参数后拼接access_token
    String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;
    //5.调用请求执行器
    try {
      T result = executor.execute(uriWithAccessToken, data);
      this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result);
      return result;
    } catch (WxErrorException e) {
      WxError error = e.getError();
      /*
       * 6.发生以下情况时尝试刷新access_token
       * 40001 获取access_token时AppSecret错误,或者access_token无效
       * 42001 access_token超时
       * 40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口
       */
      if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
        // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
        this.getWxMpConfigStorage().expireAccessToken();
        if (this.getWxMpConfigStorage().autoRefreshToken()) {
          return this.execute(executor, uri, data);
        }
      }

      if (error.getErrorCode() != 0) {
        this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error);
        throw new WxErrorException(error, e);
      }
      return null;
    } catch (IOException e) {
      this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage());
      throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e);
    }
  }

4.http请求执行器接口定义

public interface RequestExecutor {

  /**
   * 执行http请求.
   *
   * @param uri  uri
   * @param data 数据
   * @return 响应结果
   * @throws WxErrorException 自定义异常
   * @throws IOException      io异常
   */
  T execute(String uri, E data) throws WxErrorException, IOException;


  /**
   * 执行http请求.
   *
   * @param uri      uri
   * @param data     数据
   * @param handler http响应处理器
   * @throws WxErrorException 自定义异常
   * @throws IOException      io异常
   */
  void execute(String uri, E data, ResponseHandler handler) throws WxErrorException, IOException;
}

5.http请求执行器实现

  • 客户端 CloseableHttpClient client 

  •  响应   CloseableHttpResponse 

  •  参数   HttpEntity     ---  httpPost.setEntity(entity)

  •  请求   Request对象  HttpPost 

  •  配置   RequestConfig --- httpPost.setConfig(reqConfig)

  • 执行请求  response = client.execute(req);

  • 响应体   entity = response.getEntity();

  public String execute(String uri, String postEntity) throws WxErrorException, IOException {
    HttpPost httpPost = new HttpPost(uri);
    if (requestHttp.getRequestHttpProxy() != null) {
      RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
      httpPost.setConfig(config);
    }

    if (postEntity != null) {
      StringEntity entity = new StringEntity(postEntity, Consts.UTF_8);
      httpPost.setEntity(entity);
    }

    try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
      String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
      if (responseContent.isEmpty()) {
        throw new WxErrorException(WxError.builder().errorCode(9999).errorMsg("无响应内容").build());
      }

      if (responseContent.startsWith("")) {
        //xml格式输出直接返回
        return responseContent;
      }
      //如果是响应体是xml,直接返回。如果是json,包装错误码
      WxError error = WxError.fromJson(responseContent);
      if (error.getErrorCode() != 0) {
        throw new WxErrorException(error);
      }
      return responseContent;
    } finally {
      httpPost.releaseConnection();
    }
  }

 

你可能感兴趣的:(微信后台)