Webhook是一个API概念,并且变得越来越流行。我们能用事件描述的事物越多,webhook的作用范围也就越大。Webhook作为一个轻量的事件处理应用,正变得越来越有用。
准确的说webhoo是一种web回调或者http的push API,是向APP或者其他应用提供实时信息的一种方式。Webhook在数据产生时立即发送数据,也就是你能实时收到数据。这一种不同于典型的API,需要用了实时性需要足够快的轮询。这无论是对生产还是对消费者都是高效的,唯一的缺点是初始建立困难。
Webhook有时也被称为反向API,因为他提供了API规则,你需要设计要使用的API。Webhook将向你的应用发起http请求,典型的是post请求,应用程序由请求驱动。
在Android开发中会经常提交apk给测试人员进行测试,通常的做法是将构建完成的包上传至蒲公英,测试人员直接扫码下载并安装apk包从而进行测试.一般我们会将构建及发布过程自动化,可参考:
文章中实现了apk上传蒲公英后邮件通知,可是实际中,大家对邮件的关注远远没有对IM
消息的关注度高,所以接下来本文将说明,实现上传apk后自动发送钉钉消息,将更新内容,apk版本号等信息通知到测试人员
环境准备
首先环境搭建是IntelliJ+SpringMVC+Gradle
构建的,如有疑问的同学可参考IDEA+Gradle创建MyBatis+SpringMVC项目,项目中主要是对接口数据的调整及转发,实际上未用到MyBatis
,可自行进行去除🙄.
模型建立
PgyerMessage
{
"action": "应用更新",
"title": "OooPlay",
"link": "https://www.pgyer.com/oooplay_test",
"message": "您的应用OooPlay有了新的版本(2.4)更新。",
"type": "updateVersion",
"os_version": "2.4",
"build_version": "139",
"created": "2015-10-09 11:25:16",
"updated": "2015-10-09 11:25:16",
"timestamp": 1444361118,
"appsize": "2238036",
"device_type": 'iOS',
"notes": "修复了一些小弱智的小bug"
}
public class PgyerMessage {
public String action;
public String title;
public String link;
public String message;
public String type;
public String os_version;
public String build_version;
public String created;
public String updated;
public int timestamp;
public String appsize;
public String device_type;
public String notes;
@Override
public String toString() {
return "PgyerMessage{" +
"action='" + action + '\'' +
", title='" + title + '\'' +
", link='" + link + '\'' +
", message='" + message + '\'' +
", type='" + type + '\'' +
", os_version='" + os_version + '\'' +
", build_version='" + build_version + '\'' +
", created='" + created + '\'' +
", updated='" + updated + '\'' +
", timestamp=" + timestamp +
", appsize='" + appsize + '\'' +
", device_type='" + device_type + '\'' +
", notes='" + notes + '\'' +
'}';
}
}
此处有个小技巧,IDEA IntelliJ
有个好用的插件GsonFormat
可一键将Json
字符串转换为Java Model
钉钉消息则分为几种类型,具体举例可参考钉钉doc
public static final String TYPE_LINK = "link";
public static final String TYPE_MARKDOWN = "markdown";
public static final String TYPE_TEXT = "text";
public static final String TYPE_ACTIONCARD = "actionCard";
public static final String TYPE_FEEDCARD = "feedCard";
此处我们选择markdown
类型.为了便于拓展,此处将消息抽取了个基类BaseDingMessage
.
public class BaseDingMessage {
public static final String TYPE_LINK = "link";
public static final String TYPE_MARKDOWN = "markdown";
public static final String TYPE_TEXT = "text";
public static final String TYPE_ACTIONCARD = "actionCard";
public static final String TYPE_FEEDCARD = "feedCard";
public String msgtype;
public AtBean at;
public static class AtBean {
public boolean isAtAll;
public List<String> atMobiles;
}
}
public class MarkdownMessage extends BaseDingMessage {
public MarkdownBean markdown;
public static class MarkdownBean {
public String title;
public String text;
}
}
代码实现
首先在build.gradle
中导入依赖
compile group: 'com.alibaba', name: 'fastjson', version: '1.2.45'
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.9.0'
fastjson
是用力啊转化json
,okhttp
用来网络请求
spring-mvc.xml
加入json配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描控制器 -->
<context:component-scan base-package="com.lhalcyon.webhook.controller"/>
<!-- 视图渲染 -->
<bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 控制器映射器和控制器适配器 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json</value>
<value>application/xml;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 静态资源映射器 -->
<mvc:resources mapping="/statics/**" location="/WEB-INF/statics/" />
</beans>
消息发送服务DingServiceImpl.java
Service
public class DingServiceImpl implements DingService {
private static final Logger logger = Logger.getLogger(DingServiceImpl.class);
@Override
public void send(BaseDingMessage message,String url) {
MediaType jsonType = MediaType.parse("application/json; charset=utf-8");
okhttp3.RequestBody body = okhttp3.RequestBody.create(jsonType, JSON.toJSONString(message));
final Request request = new Request.Builder()
.url(url)
.post(body)
.build();
OkHttpClient client = OkhttpProvider.get();
try {
client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
蒲公英请求控制器PgyerController.java
@RestController
@RequestMapping("/pgyer")
public class PgyerController {
@Autowired
DingService dingService;
private static final Logger logger = Logger.getLogger(PgyerController.class);
@ResponseBody
@RequestMapping(value = "/update",method = RequestMethod.POST)
public BaseDingMessage apkUpdate(@RequestBody PgyerMessage pgyerMessage){
BaseDingMessage dingMessage = WebhookConverter.pgyer2Ding(pgyerMessage);
dingService.send(dingMessage, Urls.DING_TEST);
logger.info(dingMessage);
return dingMessage;
}
}
其中Urls.DING_TEST
为钉钉机器人的会话token地址,后面会说明如何创建/获取
消息转换器WebhookConverter.java
public class WebhookConverter {
private static final Logger logger = Logger.getLogger(WebhookConverter.class);
public static MarkdownMessage pgyer2Ding(PgyerMessage pgyerMessage){
MarkdownMessage message = new MarkdownMessage();
message.msgtype = BaseDingMessage.TYPE_MARKDOWN;
message.markdown = new MarkdownMessage.MarkdownBean();
message.markdown.title = pgyerMessage.device_type + "蒲公英更新";
StringBuilder builder = new StringBuilder();
builder.append("#### ").append(pgyerMessage.device_type).append("测试包已更新! \n\n")
.append("###### version: ").append(pgyerMessage.os_version).append(" | build ").append(pgyerMessage.build_version).append("\n\n")
.append("更新内容:\n").append("> ").append(pgyerMessage.notes).append("\n\n")
.append("![qr_code_test](图片地址)\n\n")
.append("[下载地址](https://www.pgyer.com/你的apk地址) 密码:你的密码\n").append(" @18810100000 @18810100001 @18818100002 ");
message.markdown.text = builder.toString();
message.at = new BaseDingMessage.AtBean();
message.at.isAtAll = false;
message.at.atMobiles = Arrays.asList("18818100000","18818100001","18818100002");
return message;
}
}
以上需自行修改内容.
然后创建钉钉机器人.创建四连
1
2
3
4
点此复制钉钉机器人会话token,建立Urls.java
.
public interface Urls {
/**
* 提测群机器人token
*/
String DING_TEST = "钉钉token";
}
此处的Ding_Test
即为上面复制的地址
至此,代码主要实现类已经完成,接下来需要去配置蒲公英webhook
配置webhook
打开蒲公英应用设置
创建webhook,写入PgyerController
的更新请求地址,如果配置与本文的相同,地址则为
http://你的地址:端口/webhook-1.0-SNAPSHOT/pgyer/update
其中webhook-1.0-SNAPSHOT
为war包在tomcat解压后的名称
done! 之后只要上传成功后,即有钉钉消息通知并@测试人员了!
让我们再看下打包后的消息通知!
类似的,代码push
,merge
也可以做成webhook消息.
Github
、Gitlab
既有成熟的对接机器人.而笔者使用的Coding是没有与钉钉做对接的,此时可自定义机器人实现,有兴趣的同学可参考上面的教程自行实现.