问题发生背景:

由于正式环境的机器,无法直接访问外网,需要通过正向代理来实现钉钉自定义机器人发送消息的功能。 很简单的一个需求,但是实现的时候发现钉钉自定义机器人提供的SDK有个bug,无法使用proxy。

代码环境:

SDK

         <dependency><groupId>com.aliyun</groupId><artifactId>alibaba-dingtalk-service-sdk</artifactId><version>1.0.1</version></dependency>
问题复现:

首先 我们通过利用SDK提供的DefaultDingTalkClient 类来调用发送消息的接口,而DefaultDingTalkClient这个类继承于DefaultTaobaoClientDefaultTaobaoClient 有 Proxy属性

public class DefaultDingTalkClient extends DefaultTaobaoClient implements DingTalkClient {****** 省略了代码
}public class DefaultTaobaoClient implements TaobaoClient {****** 省略了代码private Proxy proxy; //代理类public Proxy getProxy() {return proxy;}public void setProxy(Proxy proxy) {this.proxy = proxy;}
}

那么我们遇到的问题解决方案看似很简单,直接setProxy,将代理设置好即可实现。
实际操作后,发现报错如下

Caused by: java.net.SocketTimeoutException: connect timed outat java.net.PlainSocketImpl.socketConnect(Native Method)at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)

超时了? 登上服务器后,ping了代理的地址,是没问题的,那么问题肯定出在并没有真的使用代理去访问外网。

问题探索:

出了问题,那么看下源码怎么回事吧。跟随断点进去,DefaultDingTalkClient复写了几个DefaultTaobaoClient的方法,复写的方法中,最终要不就是调用父类,要不就是调用com.dingtalk.api.DefaultDingTalkClient#executeOApi(com.taobao.api.TaobaoRequest<T>, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) 这个方法,

public class DefaultDingTalkClient extends DefaultTaobaoClient implements DingTalkClient {public <T extends TaobaoResponse> T execute(TaobaoRequest<T> request, String accessKey, String accessSecret) throws ApiException {return execute(request, accessKey, accessSecret, null,null);}public <T extends TaobaoResponse> T execute(TaobaoRequest<T> request, String accessKey, String accessSecret, String suiteTicket) throws ApiException {return execute(request, accessKey, accessSecret, suiteTicket,null);}public <T extends TaobaoResponse> T execute(TaobaoRequest<T> request, String session) throws ApiException {if(request.getTopApiCallType() == null || request.getTopApiCallType().equals(DingTalkConstants.CALL_TYPE_TOP)) {return super.execute(request, session);} else {return executeOApi(request, session);}}public <T extends TaobaoResponse> T execute(TaobaoRequest<T> request, String accessKey, String accessSecret, String suiteTicket, String corpId) throws ApiException {if(request.getTopApiCallType() == null || request.getTopApiCallType().equals(DingTalkConstants.CALL_TYPE_TOP)) {return super.execute(request,null);} else {return executeOApi(request,null, accessKey, accessSecret,suiteTicket, corpId);}}private <T extends TaobaoResponse> T executeOApi(TaobaoRequest<T> request, String session) throws ApiException {return executeOApi(request, session, null, null, null, null);}private <T extends TaobaoResponse> T executeOApi(TaobaoRequest<T> request, String session, String accessKey, String accessSecret, String suiteTicket, String corpId) throws ApiException {*****省略代码if("GET".equals(request.getTopHttpMethod())) {data = WebV2Utils.doGet(fullUrl, appParams, connectTimeout, readTimeout);} else {// 是否需要上传文件if (request instanceof TaobaoUploadRequest) {TaobaoUploadRequest<T> uRequest = (TaobaoUploadRequest<T>) request;Map<String, FileItem> fileParams = TaobaoUtils.cleanupMap(uRequest.getFileParams());data = WebV2Utils.doPost(fullUrl, appParams, fileParams, Constants.CHARSET_UTF8, connectTimeout, readTimeout, request.getHeaderMap());} else {Map<String, Object> jsonParams = new HashMap<String, Object>();for (Map.Entry<String, String> paramEntry : appParams.entrySet()) {String key = paramEntry.getKey();String value = paramEntry.getValue();if(value.startsWith("[") && value.endsWith("]")) {List<Map<String, Object>> childMap = (List<Map<String, Object>>)TaobaoUtils.jsonToObject(value);jsonParams.put(key, childMap);} else if(value.startsWith("{") && value.endsWith("}")) {Map<String, Object> childMap = (Map<String, Object>)TaobaoUtils.jsonToObject(value);jsonParams.put(key, childMap);} else {jsonParams.put(key, value);}}data = WebV2Utils.doPostWithJson(fullUrl, jsonParams, Constants.CHARSET_UTF8, connectTimeout, readTimeout);}}}
}

我们选择其中一个分支来分析,另一个分支同理。
可以看到WebV2Utils.doPostWithJson 这个方法才是执行的关键,这个方法内部跟进去看看。

 public static HttpResponseData doPostWithJson(String url, Map<String, Object> params, String charset, int connectTimeout, int readTimeout) throws IOException {String ctype = "application/json;charset=" + charset;byte[] content = {};String body = TaobaoUtils.objectToJson(params);if (body != null) {content = body.getBytes(charset);}return _doPost(url, ctype, content, connectTimeout, readTimeout, null, null);}private static HttpResponseData _doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout, Map<String, String> headerMap, Proxy proxy) throws IOException {***** 省略代码}

看到了吗, 他直接将Proxy参数,传了null,而非从赋值的属性中获取。

解决方案:

既然发现是他自作主张,将Proxy直接传入了null,那么我们只需要自己复写一个方法,传入赋值好的Proxy即可。

public class DefaultProxyDingTalkClient extends DefaultDingTalkClient {private <T extends TaobaoResponse> T executeOApi(TaobaoRequest<T> request, String session, String accessKey, String accessSecret, String suiteTicket, String corpId) throws ApiException {*****省略代码if("GET".equals(request.getTopHttpMethod())) {data = WebV2Utils.doGet(fullUrl, appParams, connectTimeout, readTimeout);} else {// 是否需要上传文件if (request instanceof TaobaoUploadRequest) {TaobaoUploadRequest<T> uRequest = (TaobaoUploadRequest<T>) request;Map<String, FileItem> fileParams = TaobaoUtils.cleanupMap(uRequest.getFileParams());data = WebV2Utils.doPost(fullUrl, appParams, fileParams, Constants.CHARSET_UTF8, connectTimeout, readTimeout, request.getHeaderMap());} else {Map<String, Object> jsonParams = new HashMap<String, Object>();for (Map.Entry<String, String> paramEntry : appParams.entrySet()) {String key = paramEntry.getKey();String value = paramEntry.getValue();if(value.startsWith("[") && value.endsWith("]")) {List<Map<String, Object>> childMap = (List<Map<String, Object>>)TaobaoUtils.jsonToObject(value);jsonParams.put(key, childMap);} else if(value.startsWith("{") && value.endsWith("}")) {Map<String, Object> childMap = (Map<String, Object>)TaobaoUtils.jsonToObject(value);jsonParams.put(key, childMap);} else {jsonParams.put(key, value);}}data = doPostWithJson(fullUrl, jsonParams, Constants.CHARSET_UTF8, connectTimeout, readTimeout);}}}}
}private HttpResponseData doPostWithJson(String url, Map<String, Object> params, String charset, int connectTimeout, int readTimeout) throws IOException {String ctype = "application/json;charset=" + charset;byte[] content = {};String body = TaobaoUtils.objectToJson(params);if (body != null) {content = body.getBytes(charset);}return WebV2Utils.doPost(url, ctype, content, connectTimeout, readTimeout, null, this.getProxy());}

完美解决问题。

附上自己封装好的钉钉自定义机器人代码

地址

接入步骤:

  1. 引入依赖

        <dependency><groupId>com.aliyun</groupId><artifactId>alibaba-dingtalk-service-sdk</artifactId><version>1.0.1</version></dependency>
    
  2. 配置webhook 和 正向代理(非必填,不填时使用默认发送)
    ding:webhook: 钉钉机器人复制的webhookproxyIpList: "host1:port1,host2:port2"#proxyIpList: "host1:port1"
    
  3. 更改DingNotifyManager#DINGDING_PREFIX
    把前缀改成钉钉机器人配置的自定义关键词

使用:
使用方法封装了三个较常用的

  1. 发送纯文本 DingNotifyManager#sendCleanText
  2. 发送带连接的通知 DingNotifyManager#sendLink
  3. 发送markdown格式通知 DingNotifyManager#sendMarkdown

钉钉自定义机器人无法指定正向代理问题解决相关推荐

  1. 机器人聊天软件c#_使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_132 最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用 ...

  2. 使用python3.7配置开发钉钉群自定义机器人(2020年新版攻略)

    最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用,真的让人觉得阿里的pm都是脑残才会设计出这种脑残产品,不过吐槽归吐槽,该用 ...

  3. 最新教程:Python开发钉钉群自定义机器人

    最近疫情比较严重,很多公司依靠阿里旗下的办公软件钉钉来进行远程办公,当然了,钉钉这个产品真的是让人一言难尽,要多难用有多难用,真的让人觉得阿里的pm都是脑残才会设计出这种脑残产品,不过吐槽归吐槽,该用 ...

  4. 通过Webhook接入钉钉群自定义机器人

    在钉钉群中添加Webhook自定义机器人, 复制Webhook地址保存: https://oapi.dingtalk.com/robot/send?access_token=xxxxxx 安全设置:选 ...

  5. Java实现钉钉自定义机器人接入

    Java实现钉钉自定义机器人接入 1. 钉钉自定义机器人接入概述 1.1 钉钉机器人简介 1.2 自定义机器人接入 2. 钉钉自定义机器人接入实现 2.1 场景介绍 2.2 调用频率限制 2.3 创建 ...

  6. 钉钉自定义机器人python_使用钉钉自定义机器人发送舔狗日记[70行][python]

    [Python] 纯文本查看 复制代码# -*- coding: utf-8 -*- import requests import json import time #下面是解密用到的模块 impor ...

  7. 超火的钉钉自定义机器人原来是这么设置的

    企业内部有较多系统支撑着公司的核心业务流程,譬如CRM系统.交易系统.监控报警系统等等.通过钉钉的自定义机器人,可以将这些系统事件同步到钉钉的聊天群 接入自定义机器人很简单,大概以下几步 点击群设置选 ...

  8. java调用钉钉的群自定义机器人

    1.创建钉钉群自定义机器人 2.创建好之后钉钉会返回wobhook地址,这个地址用来访问钉钉接口,如果之前选择了加签,这个地址需要再处理,否则不需要 .(点击设置说明按钮,进入钉钉的官方文档) 3.查 ...

  9. 让服务器实时跟你报告 —— 钉钉机器人 企业微信机器人

    写在前面 一入炼丹深似海,希望天下没有空闲的GPU 每次训练开始跑起来总是要不断看什么时候结束,永远副屏都要开着终端实时查看GPU占用情况 原本想着能不能服务器有空闲时给我发邮件提醒呢,简单搜索了一番 ...

最新文章

  1. numpy.matmul处理一维数组的 3维以上的性质
  2. AI可以在游戏里称霸,但是解决现实问题太难了
  3. 计算4位数每位数相加之和(Python)
  4. 比微软kinect更强的视频跟踪算法--TLD跟踪算法介绍
  5. C语言 变量声明和定义的区别
  6. JS将数字转换为中文
  7. GCC9.2/Python3.8/Libvirt6.0/QEMU4.2 编译/配置/安装
  8. python中tmp什么意思_python中temp是什么意思-问答-阿里云开发者社区-阿里云
  9. linux进入超级管理员权限,一直处于超级管理员权限下
  10. common,Google Guava,Guice
  11. vector public member function
  12. java冒泡排序算法代码降序_Java排序算法总结之冒泡排序
  13. QQ音乐JS逆向爬虫,获取调皮的sign参数,我用python全都爬!
  14. npp php,Notepad++ Home下载,NPP官方下载
  15. 手把手教你接入快应用Push
  16. 区块链在版权保护方面的探索与实践
  17. java万年历制作_【转】用EXCEL制作一份万年历方法
  18. 跨专业转行数据分析真的可行吗?
  19. __virtual__ returned False: cannot import name certs ---saltstack异常解决
  20. 正则表达式忽略大小写

热门文章

  1. ubuntu20.04开通多个ssh端口
  2. 实体商家也能玩转月活10亿的微信小程序生态
  3. 摔倒了不可怕,可怕的是爬不起
  4. Thumbnails 压缩后反而变大
  5. ADB 操作命令及用法
  6. 回归模型的score得分为负_SPSS中 回归 B值为负数什么意思
  7. 使用Oracle VM VirtualBox完成Linux环境搭建openEuler
  8. android 蓝牙搜索不到Ble设备
  9. JavaScript之算法
  10. 计算机网络系统工程技术要求,《SZDBZ 5.4-2008 信息系统工程建设技术规范 第4部分 计算机网络系统工程》.pdf...