通过企业微信服务端API接口进行群发应用消息
通过企业微信服务端API接口进行群发应用消息一.基本术语介绍说明1.corpid2.userid3.部门id4.tagid最近项目上需要用到企业微信应用进行消息的群发功能,用来将系统产生的告警消息通过企业微信群发的方式通知客户方,来达到及时处理故障的目的,使用了下还是较简单的,这里记录一下过程,备查。一.基本术语介绍说明1.corpid每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的
最近项目上需要用到企业微信应用进行消息的群发功能,用来将系统产生的告警消息通过企业微信群发的方式通知客户方,来达到及时处理故障的目的,使用了下还是较简单的,这里记录一下过程,备查。文章前面有一点术语的介绍,如果想快速直接看怎么使用,点击这里跳过基本术语介绍,直接开始。
一.基本术语介绍说明
1.corpid
每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID”(需要有管理员权限)
2.userid
每个成员都有唯一的userid,即所谓“帐号”。在管理后台->“通讯录”->点进某个成员的详情页,可以看到。
3.部门id
每个部门都有唯一的id,在管理后台->“通讯录”->“组织架构”->点击某个部门右边的小圆点可以看到
4.tagid
每个标签都有唯一的标签id,在管理后台->“通讯录”->“标签”,选中某个标签,在右上角会有“标签详情”按钮,点击即可看到
5.agentid
每个应用都有唯一的agentid。在管理后台->“应用与小程序”->“应用”,点进某个应用,即可看到agentid。
6.secret
secret是企业应用里面用于保障数据安全的“钥匙”,每一个应用都有一个独立的访问密钥,为了保证数据的安全,secret务必不能泄漏。
自建应用secret。在管理后台->“应用与小程序”->“应用”->“自建”,点进某个应用,即可看到。
基础应用secret。某些基础应用(如“审批”“打卡”应用),支持通过API进行操作。在管理后台->“应用与小程序”->“应用->”“基础”,点进某个应用,点开“API”小按钮,即可看到。
通讯录管理secret。在“管理工具”-“通讯录同步”里面查看(需开启“API接口同步”);
客户联系管理secret。在“客户联系”栏,点开“API”小按钮,即可看到。
7.access_token
access_token是企业后台去企业微信的后台获取信息时的重要票据,由corpid和secret产生。所有接口在通信时都需要携带此信息用于验证接口的访问权限
二.通过企业微信服务端API群发应用消息
1.Http接口调用工具类准备
因为需要用到Http调用,这里提前准备2个Http的调用工具,一个是J
ava的Jdk自带实现的
,不依赖任何jar包,可用于测试,如果你的调用量不多,也可以用于生产。另一个是依赖apache的httpClient
,具备连接池功能,建议可用于生产。因为代码占内容太多,因此代码位于文章最后,可直接跳转至代码部署查看,点击即会跳转
2.获取access_token
官方接口文档:
https://work.weixin.qq.com/api/doc/90000/90135/91039
请求方式:GET(HTTPS)
请求URL:https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
注:通常正式环境需要自己登陆后自行维护access_token,一般是2小时有效期,在快到期或已过期时需要重新登陆,可以用利用返回结果码为40014和42001分别表示为非法access_token和过期的access_token,可以利用此两个错误码进行判断与更新
测试代码:
使用纯Jdk实现的工具类进行调试:
public static void main(String[] args) throws Exception {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww160d****&corpsecret=BAtr2B***************";
HttpRestUtils util = new HttpRestUtils();
String result = util.doGet(url);
System.out.println(result);
}
或者使用apache的httpClient的工具类调试:
public static void main(String[] args) throws InterruptedException {
//这里把你的corpid和corpsecret替换成正确的
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ww160d****&corpsecret=BAtr2B***************";
System.out.println(HttpsKit.get(url));
//关闭连接池,正式环境中这个不要关闭
HttpsKit.closeConnectionPool();
}
响应接果:
{"errcode":0,"errmsg":"ok","access_token":"vMpg1HNU8PHf0qqNSfVGMXw2Gg0HN16LnvMH3J4LXeoY5MMA25PiO2ZabcdHJ6bRi5PqUuyLf94aRBb3yTKs344h5eU35doprLeIKtuf9xfKOk8VQ6F_GeTuxmcV_qQH0CLOrc5y9cXT9SCEi7LpQCiS4F4ssdff0zu-jyGmlEtUBplqSF8xDQBJ3aj6-hfg","expires_in":7200}
3.调用群应用消息发送接口即可
群应用消息发送接口官方文档:
https://work.weixin.qq.com/api/doc/90000/90135/90236
应用支持推送文本、图片、视频、文件、图文等类型。
请求方式:POST(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN
以文本消息为例,access_token在请求地址里面,其他的参数就是以json方式封装,其他的参数可参看详情的上面的文档获取。比如以下截图即从官方文档中获取到的
测试代码如下:
使用纯Jdk实现的工具类进行调试:
public static void main(String[] args) throws Exception {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=JhLKJB2cZssssssssssssssssssssssssssssssssssssU2HHQ";
HttpRestUtils util = new HttpRestUtils();
String paramStr = "{\"touser\":\"@all\",\"msgtype\":\"text\",\"agentid\":1000001,\"text\":{\"content\":\"测试下应用群发告警消息,打扰了,请忽略。\"}}";
String result = util.doPost(url, paramStr);
System.out.println(result);
}
或者使用apache的httpClient的工具类调试:
public static void main(String[] args) throws InterruptedException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=JhLKJB2cZssssssssssssssssssssssssssssssssssssU2HHQ";
String data = "{\"touser\":\"@all\",\"msgtype\":\"text\",\"agentid\":1000001,\"text\":{\"content\":\"测试下应用群发告警消息,打扰了,请忽略。\"}}";
System.out.println(HttpsKit.postJson(url, data));
//关闭连接池,正式环境中这个不要关闭
HttpsKit.closeConnectionPool();
}
4.Http接口调用工具类代码提供如下
工具类1
:使用纯jdk自带的URLConnection实现的
package cn.gzsendi.system.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpRestUtils {
private final String GET = "GET";
private final String POST = "POST";
private final String CharacterSet = "UTF-8";
public String doGet(String url) throws Exception{
String method = this.GET;
return load(url,null,method);
}
public String doPost(String url, String params) throws Exception{
String method = this.POST;
return load(url,params,method);
}
/**
* @param url
* @param params
* @param method
* @return
* @throws Exception
*/
public String load(String url,String params,String method) throws Exception {
HttpURLConnection conn = null;
try {
boolean isHttps = url.startsWith("https");
URL restURL = new URL(url);
conn = (HttpURLConnection) restURL.openConnection();
//https请求需要格外处理下
if(isHttps) {
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
SSLSocketFactory ssf = sslContext.getSocketFactory();
((HttpsURLConnection)conn).setSSLSocketFactory(ssf);
((HttpsURLConnection)conn).setHostnameVerifier(new TrustAnyHostnameVerifier());
}
conn.setDoOutput(true);
conn.setAllowUserInteraction(false);
conn.setUseCaches(false);
conn.setRequestMethod(method);
conn.connect();
OutputStreamWriter out = null;
OutputStream outputStream = null;
if(this.POST.equals(method) && params != null){
outputStream = conn.getOutputStream();
out = new OutputStreamWriter(outputStream, this.CharacterSet);
out.write(params);
out.close();
}
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, this.CharacterSet);
BufferedReader bReader = new BufferedReader(inputStreamReader);
String line = "";
StringBuffer resultStr = new StringBuffer();
while (null != (line = bReader.readLine())) {
resultStr.append(line);
}
// 释放资源
bReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
if(out!=null) out.close();
if(outputStream!=null)outputStream.close();
return resultStr.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(conn != null) conn.disconnect();
}
return null;
}
private class TrustAnyHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
// 直接返回true
return true;
}
}
private class MyX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
工具类2
:使用apache的httpclient进行编写实现类
注:pom依赖,我的工具类里面用到了common-io,httpclient,和log4j,其中log4j如果你不想要,可以根据代码进行调整删除,common-io也是一样,也可以采用其他方法,修改点代码就可。
<!-- httpclient start -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.12</version>
</dependency>
<!-- httpclient end -->
<!-- common-io start -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<!-- common-io end -->
<!-- log start -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- log end -->
package cn.gzsendi.system.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.IOUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpsKit {
private static Logger logger = LoggerFactory.getLogger(HttpsKit.class);
private static final int CONNECT_TIMEOUT = 10000;// 设置连接建立的超时时间为10000ms
private static final int SOCKET_TIMEOUT = 30000; // 多少时间没有数据传输
private static final int HttpIdelTimeout = 30000;//空闲时间
private static final int HttpMonitorInterval = 10000;//多久检查一次
private static final int MAX_CONN = 200; // 最大连接数
private static final int Max_PRE_ROUTE = 200; //设置到路由的最大连接数,
private static CloseableHttpClient httpClient; // 发送请求的客户端单例
private static PoolingHttpClientConnectionManager manager; // 连接池管理类
private static ScheduledExecutorService monitorExecutor;
private static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
private static final String APPLICATION_JSON = "application/json";
private final static Object syncLock = new Object(); // 相当于线程锁,用于线程安全
private static RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(CONNECT_TIMEOUT)
.setConnectTimeout(CONNECT_TIMEOUT)
.setSocketTimeout(SOCKET_TIMEOUT).build();
private static CloseableHttpClient getHttpClient() {
if (httpClient == null) {
// 多线程下多个线程同时调用getHttpClient容易导致重复创建httpClient对象的问题,所以加上了同步锁
synchronized (syncLock) {
if (httpClient == null) {
try {
httpClient = createHttpClient();
} catch (KeyManagementException e) {
logger.error("error",e);
} catch (NoSuchAlgorithmException e) {
logger.error("error",e);
} catch (KeyStoreException e) {
logger.error("error",e);
}
// 开启监控线程,对异常和空闲线程进行关闭
monitorExecutor = Executors.newScheduledThreadPool(1);
monitorExecutor.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// 关闭异常连接
manager.closeExpiredConnections();
// 关闭5s空闲的连接
manager.closeIdleConnections(HttpIdelTimeout,TimeUnit.MILLISECONDS);
logger.info(manager.getTotalStats().toString());
//logger.info("close expired and idle for over "+HttpIdelTimeout+"ms connection");
}
}, HttpMonitorInterval, HttpMonitorInterval, TimeUnit.MILLISECONDS);
}
}
}
return httpClient;
}
/**
* 构建httpclient实例
* @return
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
private static CloseableHttpClient createHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
SSLContextBuilder builder = new SSLContextBuilder();
// 全部信任 不做身份鉴定
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
});
ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory> create()
.register("http", plainSocketFactory)
.register("https", sslSocketFactory).build();
manager = new PoolingHttpClientConnectionManager(registry);
// 设置连接参数
manager.setMaxTotal(MAX_CONN); // 最大连接数
manager.setDefaultMaxPerRoute(Max_PRE_ROUTE); // 路由最大连接数
// 请求失败时,进行请求重试
HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
if (i > 3) {
// 重试超过3次,放弃请求
logger.error("retry has more than 3 time, give up request");
return false;
}
if (e instanceof NoHttpResponseException) {
// 服务器没有响应,可能是服务器断开了连接,应该重试
logger.error("receive no response from server, retry");
return true;
}
if (e instanceof SSLHandshakeException) {
// SSL握手异常
logger.error("SSL hand shake exception");
return false;
}
if (e instanceof InterruptedIOException) {
// 超时
logger.error("InterruptedIOException");
return false;
}
if (e instanceof UnknownHostException) {
// 服务器不可达
logger.error("server host unknown");
return false;
}
if (e instanceof ConnectTimeoutException) {
// 连接超时
logger.error("Connection Time out");
return false;
}
if (e instanceof SSLException) {
logger.error("SSLException");
return false;
}
HttpClientContext context = HttpClientContext.adapt(httpContext);
HttpRequest request = context.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
// 如果请求不是关闭连接的请求
return true;
}
return false;
}
};
CloseableHttpClient client = HttpClients.custom().setConnectionManager(manager).setRetryHandler(handler).build();
return client;
}
public static String get(String url) {
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36");
httpGet.setConfig(requestConfig);
CloseableHttpResponse response = null;
InputStream in = null;
String result = null;
try {
response = getHttpClient().execute(httpGet,HttpClientContext.create());
HttpEntity entity = response.getEntity();
if (entity != null) {
in = entity.getContent();
result = IOUtils.toString(in, "utf-8");
}
} catch (IOException e) {
logger.error("error",e);
} finally {
try {
if (in != null) in.close();
} catch (IOException e) {
logger.error("error",e);
}
try {
if (response != null) response.close();
} catch (IOException e) {
logger.error("error",e);
}
}
return result;
}
public static String postJson(String url,Map<String,Object> requestParams) {
return postJson(url, JsonUtil.toJSONString(requestParams));
}
public static String postJson(String url,Map<String,Object> requestParams,Map<String,Object> headerParams) {
return postJson(url, JsonUtil.toJSONString(requestParams),headerParams);
}
public static String postJson(String url,String requestParamStr) {
return postJson(url, requestParamStr, null);
}
public static String postJson(String url,String requestParamStr,Map<String,Object> headerParams) {
HttpPost httppost = new HttpPost(url);
httppost.setHeader("Content-Type", APPLICATION_JSON+";charset=" + CharEncoding.UTF_8);
httppost.setHeader("Accept",APPLICATION_JSON+";charset=" +CharEncoding.UTF_8);
httppost.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36");
if(headerParams != null && headerParams.size()>0){
for(String headerName : headerParams.keySet()) {
httppost.setHeader(headerName,headerParams.get(headerName)+"");
}
}
StringEntity se = new StringEntity(requestParamStr,CharEncoding.UTF_8);
se.setContentType(APPLICATION_JSON+";charset=" +CharEncoding.UTF_8);
httppost.setEntity(se);
httppost.setConfig(requestConfig);
CloseableHttpResponse response = null;
InputStream in = null;
String result = null;
try {
response = getHttpClient().execute(httppost,HttpClientContext.create());
HttpEntity entity = response.getEntity();
if (entity != null) {
in = entity.getContent();
result = IOUtils.toString(in, "utf-8");
}
} catch (IOException e) {
logger.error("error",e);
} finally {
try {
if (in != null) in.close();
} catch (IOException e) {
logger.error("error",e);
}
try {
if (response != null) response.close();
} catch (IOException e) {
logger.error("error",e);
}
}
return result;
}
//requestParamStr---------->>> name=test&age=12
public static String postFormUrlencoded(String url,String requestParamStr) {
return postFormUrlencoded(url, requestParamStr ,null);
}
public static String postFormUrlencoded(String url,String requestParamStr,Map<String,Object> headerParams) {
Map<String,String> requestParams = new HashMap<String,String>();
String[] strs = requestParamStr.split("&");
for(String str : strs) {
String[] keyValues = str.split("=");
if(keyValues.length == 2) {
requestParams.put(keyValues[0], keyValues[1]);
}
}
return postFormUrlencoded(url, requestParams,headerParams);
}
public static String postFormUrlencoded(String url,Map<String,String> requestParams) {
return postFormUrlencoded(url,requestParams,null);
}
public static String postFormUrlencoded(String url,Map<String,String> requestParams,Map<String,Object> headerParams) {
HttpPost httppost = new HttpPost(url);
//application/json
httppost.setHeader("Content-Type", APPLICATION_FORM_URLENCODED+";charset=" + CharEncoding.UTF_8);
httppost.setHeader("Accept",APPLICATION_FORM_URLENCODED+";charset=" +CharEncoding.UTF_8);
httppost.setHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36");
if(headerParams != null && headerParams.size()>0){
for(String headerName : headerParams.keySet()) {
httppost.setHeader(headerName,headerParams.get(headerName)+"");
}
}
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
for(String keyStr : requestParams.keySet()) {
formparams.add(new BasicNameValuePair(keyStr, requestParams.get(keyStr)));
}
UrlEncodedFormEntity uefe = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
httppost.setEntity(uefe);
httppost.setConfig(requestConfig);
CloseableHttpResponse response = null;
InputStream in = null;
String result = null;
try {
response = getHttpClient().execute(httppost,HttpClientContext.create());
HttpEntity entity = response.getEntity();
if (entity != null) {
in = entity.getContent();
result = IOUtils.toString(in, "utf-8");
}
} catch (IOException e) {
logger.error("error",e);
} finally {
try {
if (in != null) in.close();
if (response != null) response.close();
} catch (IOException e) {
logger.error("error",e);
}
}
return result;
}
/**
* 关闭连接池
*/
public static void closeConnectionPool() {
try {
if(httpClient != null) httpClient.close();
if(manager != null) manager.close();
if(monitorExecutor != null) monitorExecutor.shutdown();
} catch (IOException e) {
logger.error("error",e);
}
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)