Java服务端集成环信IM
由于业务需求,需要在系统中集成环信,所以去看了下官方文档,写篇博客分享一下。
在开始之前,我们需要了解一下什么是REST。
REST(Representational State Transfer)是一种轻量级的 Web Service 架构风格,可以翻译成“表述性状态转移”,实现和操作明显比 SOAP 和 XML-RPC 更为简洁,可以完全通过 HTTP 协议实现,还可以利用缓存 Cache 来提高响应速度,性能、效率和易用性上都优于 SOAP 协议。
REST 架构遵循了 CRUD 原则,CRUD原则对于资源只需要四种行为:Create(创建)、Read(读取)、Update(更新)和Delete(删除)就可以完成对其操作和处理。这四个操作是一种原子操作,对资源的操作包括获取、创建、修改和删除资源的操作正好对应HTTP 协议提供的 GET、POST、PUT 和 DELETE 方法,因此 REST 把 HTTP 对一个 URL 资源的操作限制在POST、GET、PUT 和 DELETE 这四个之内。这种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
我看到一篇关于RESTful的很不错的文章:RESTful API 设计指南,有兴趣的可以看下,讲的很好。
环信 REST 平台
环信 REST 平台提供的是一个多租户用户体系,资源以集合(Collection)的形式来描述,这里所说的 Collection 包括 DataBase、企业(orgs)、应用(apps)、IM用户(users)、群组(chatgroups)、消息(chatmessages)、文件(chatfiles)等等,之间的包含关系是:
DB = {org1, org2, …}
org = {app1, app2, …}
app = {users, messages, chatfiles, chatmessages, chatgroups, …}
users = {user1, user2, …}
messages = {message1, message2, …}
chatfiles = {chatfile1, chatfile2, …}
chatmessages = {chatmessage1, chatmessage2, …}
chatgroups = {group1, group2, …}
多租户是指软件架构支持一个实例服务多个用户(Customer),每一个用户被称之为租户(Tenant),软件给予租户可以对系统进行部分定制的能力,如用户界面颜色或业务规则,但是他们不能定制修改软件的代码。详情可查看官方文档:服务端集成。
在环信服务体系中,不同org之间的用户数据相互隔离,同一个 org 下不同 APP 之间的用户数据相互隔离。
REST server
环信的服务器端接口都是通过REST服务方式提供的,REST API基于最简单的HTTP协议,在各个编程语言中都提供了良好的支持。
REST client
REST client 就是调用 REST API 的程序端,调用方式有多种:Linux curl、浏览器、编程语言 HTTP 请求访问实现等。
调用 REST API,本质就是发送 HTTP 请求,只不过大家常用的可能是 HTTP GET 和 HTTP POST 请求,但是在 REST 里面还经常用到 HTTP PUT 和 HTTP DELETE。在 REST 中,把这四种操作称之为动词,可以(但不是特别准确)想象成增删改查。
而动词所操作的对象,在 REST 中,被称之为“资源”,也就是 URL,而这些也都是标准的 HTTP 协议的内容。实际上,当我们在浏览器中打开一个网站的时候,例如,打开环信官网,浏览器实际上发送给网站服务器的,就是一个 HTTP GET 的请求。
需要注意的是,环信的 REST API 都是基于 JSON 的,所以在构造 HTTP 请求的时候,需要在 HTTP HEADER 中指明:
Header_name | Header_value | Description |
---|---|---|
Accept | application/json | 服务器端返回给客户端的数据类型 |
Content-Type | application/json | 客户端发送到服务器端的数据类型 |
JAVA
在 Java 中,REST client 实现方式有多种,比如 JBOss RestEasy、Sun Jersey、Dropwizard、Apache HTTPClient。
本文用的是Spring的RestTemplate。有一篇不错的博客:Spring RestTemplate中几种常见的请求方式,可以去看看。
在集成之前,我们得在环信上注册并创建一个账号。
注册开发者账号
第 1 步:进入环信官网,选择“注册 > 注册即时通讯云”,进入环信管理后台的注册页面。
第 2 步:在注册页面中,填写详细资料,并点击“注册”按钮。
注册成功后,我们会向您填写的邮箱中发送验证信息,请前往邮箱进行账号激活。
创建应用
账号激活成功后,回到控制台登录页面登录到开发者后台。
第 1 步:在我的应用中,点击创建应用按钮,如下图:
第 2 步:填写创建应用的名称(内容只限于数字、大小写字母),如下图:
(应用名称会存在于你生成的 AppKey 中,如:测试 Demo 中 AppKey 为 easemob-demo#chatdemo,则 chatdemo 为填写的应用名称。注册授权根据需要自行选择,AppKey的长度限制为1k字节以内。)
第 3 步:填写好应用名称后,点确定。创建成功,系统会为你生成 AppKey 以及相关配置信息,如下图:
之后,将此 AppKey 配置到 Android、iOS、Web IM 上即可。
名词 | 解释 |
---|---|
org_name | 企业的唯一标识,开发者在环信开发者管理后台注册账号时填写的企业 ID |
app_name | 同一“企业”下“APP”唯一标识,开发者在环信开发者管理后台创建应用时填写的“应用名称” |
org_admin | 开发者在环信开发者管理后台注册时填写的“用户名”,企业管理员拥有对该企业账号下所有资源的操作权限 |
AppKey | 一个 APP 的唯一标识,规则是 ${org_name} # ${app_name} |
这些在官方文档上都是有的,可点击查看:注册并创建应用
编写代码
接下来就开始用户集成了,详细的就不说了,可点击查看用户体系集成。
主要把写好的工具类分享一下。在对应的场景,只需调用对应的方法即可。
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** 环信工具类*/
public class HXUtil {private static RestTemplate restTemplate = new RestTemplate();// 企业的唯一标识,开发者在环信开发者管理后台注册账号时填写的企业 IDprivate static final String ORG_NAME = "1122161011178276";// App的client_idprivate static final String CLIENT_ID = "YXA6Irz_oI-GEead-FFvbfaMbQ";// App的client_secretprivate static final String CLIENT_SECRET = "YXA6VsR5JypETS3iPFvNNxYklmho0Vw";// 同一“企业”下“APP”唯一标识,开发者在环信开发者管理后台创建应用时填写的“应用名称”private static final String APP_NAME = "testapp";// 链接前缀private static final String URL_PREFIX = "http://a1.easemob.com/" + ORG_NAME + "/" + APP_NAME + "/";// 缓存的tokenprivate static Token token;// token的失效时间private static long expiresTime;public enum HXMessageType {txt,// 文本img,// 图片loc,// 位置audio,// 音频video,// 视频file// 文件}/*** 获取Token* 注意:关于有效时间,我在网上找过,说的是7天,但是返回的是5184000,* 但是官网上说是以秒为单位,这么算下来就是60天了,* 觉得不太对,就先将有效时间设为了7天* @return token*/public static Token getToken() {// 判断Token是否已经过期,如果过期需要重新获取if (token == null || expiresTime < new Date().getTime()) {try {JSONObject body = new JSONObject();body.put("grant_type", "client_credentials");body.put("client_id", CLIENT_ID );body.put("client_secret", CLIENT_SECRET );HttpEntity httpEntity = new HttpEntity(body.toString(), null);ResponseEntity<Token> tokenResponseEntity = restTemplate.postForEntity(URL_PREFIX + "token", httpEntity, Token.class);token = tokenResponseEntity.getBody();// 设置7天后过期Calendar c = Calendar.getInstance();c.add(Calendar.DATE, 7);expiresTime = c.getTime().getTime();} catch (RestClientException e) {e.printStackTrace();}}return token;}/*** 添加用户** @param username 用户名(唯一非空)* @param password 密码* @return 是否成功*/public static boolean addUser(String username, String password) {try {JSONArray body = new JSONArray();JSONObject jsonObject = new JSONObject();jsonObject.put("username", username);jsonObject.put("password", password);body.add(jsonObject);HttpEntity httpEntity = new HttpEntity(body.toString(), null);ResponseEntity responseEntity = restTemplate.postForEntity(URL_PREFIX + "users", httpEntity, null);return responseEntity.getStatusCodeValue() == 200;} catch (RestClientException e) {e.printStackTrace();return false;}}/*** 修改用户密码** @param username 用户名* @param newpassword 新密码* @return 是否成功*/public static boolean updatePassword(String username, String newpassword) {try {JSONObject body = new JSONObject();body.put("newpassword", newpassword);HttpHeaders headers = new HttpHeaders();headers.add("Authorization", "Bearer " + getToken().getAccess_token());HttpEntity httpEntity = new HttpEntity(body.toString(), headers);ResponseEntity responseEntity = restTemplate.postForEntity(URL_PREFIX + "users/{username}/password", httpEntity, null, username);System.out.println(responseEntity.getStatusCodeValue());return responseEntity.getStatusCodeValue() == 200;} catch (RestClientException e) {e.printStackTrace();return false;}}/*** 删除用户** @param username 用户名*/public static boolean deleteUser(String username) {try {HttpEntity httpEntity = new HttpEntity(null, getHttpHeaders(MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON));ResponseEntity<HXUser> responseEntity = restTemplate.exchange(URL_PREFIX + "users/{username}", HttpMethod.DELETE, httpEntity, HXUser.class, username);System.out.println(responseEntity.getStatusCodeValue());return responseEntity.getStatusCodeValue() == 200;} catch (RestClientException e) {e.printStackTrace();return false;}}/*** 添加好友** @param ownerUsername 用户名* @param friendName 好友用户名* @return 是否成功*/public static boolean addFriend(String ownerUsername, String friendName) {try {HttpEntity httpEntity = new HttpEntity(null, getHttpHeaders(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON));ResponseEntity responseEntity = restTemplate.postForEntity(URL_PREFIX + "users/{owner_username}/contacts/users/{friend_username}", httpEntity, HXUser.class, ownerUsername, friendName);System.out.println(responseEntity.getStatusCodeValue());return responseEntity.getStatusCodeValue() == 200;} catch (RestClientException e) {e.printStackTrace();return false;}}/*** 删除好友** @param ownerUsername 用户名* @param friendName 好友用户名* @return 是否成功*/public static boolean deleteFriend(String ownerUsername, String friendName) {try {HttpEntity httpEntity = new HttpEntity(null, getHttpHeaders(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON));ResponseEntity responseEntity = restTemplate.exchange(URL_PREFIX + "users/{owner_username}/contacts/users/{friend_username}", HttpMethod.DELETE, httpEntity, HXUser.class, ownerUsername, friendName);System.out.println(responseEntity.getStatusCodeValue());return responseEntity.getStatusCodeValue() == 200;} catch (RestClientException e) {e.printStackTrace();return false;}}/*** 发送消息** @param sendUser 发送用户* @param targetUser 接收用户* @param msg 发送消息* @return 是否成功*/public static boolean sendToUser(String sendUser, String targetUser, String msg) {try {JSONObject body = new JSONObject();body.put("target_type", "users");JSONArray targetUserjson = new JSONArray();targetUserjson.add(targetUser);body.put("target", targetUserjson);JSONObject msgJson = new JSONObject();msgJson.put("type", HXMessageType.txt.name());msgJson.put("msg", msg);body.put("msg", msgJson);body.put("from", sendUser);HttpEntity httpEntity = new HttpEntity(body, getHttpHeaders(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON));ResponseEntity responseEntity = restTemplate.postForEntity(URL_PREFIX + "messages", httpEntity, null);System.out.println(responseEntity.getStatusCodeValue());return responseEntity.getStatusCodeValue() == 200;} catch (RestClientException e) {e.printStackTrace();return false;}}/*** 获取HttpHeaders** @param contentType 客户端发送类型* @param accept 响应类型* @return HttpHeaders*/private static HttpHeaders getHttpHeaders(MediaType contentType, MediaType... accept) {HttpHeaders headers = new HttpHeaders();headers.add("Authorization", "Bearer " + getToken().getAccess_token());headers.setContentType(contentType != null ? contentType : MediaType.APPLICATION_JSON);headers.setAccept(Arrays.asList((accept != null && accept.length > 0) ? accept : new MediaType[]{MediaType.APPLICATION_JSON}));return headers;}}
这里用到了两个实体类:HXUser、Token。
HXUser:
public class HXUser {private String uuid; // 用户的UUID,标识字段private String type; // 类型,“user”用户类型private Long created;private Long modified;private String username; // 用户名,也就是环信 ID,(唯一,非空)private String nickName; // 昵称private boolean activated; // 用户是否已激活,“true”已激活,“false“封禁,封禁需要通过解禁接口进行解禁,才能正常登录public String getUuid() {return uuid;}public void setUuid(String uuid) {this.uuid = uuid;}
}
Token:
public class Token {private String access_token; // 有效的token字符串private String expires_in; // token 有效时间,以秒为单位,在有效期内不需要重复获取private String application; // 当前 App 的 UUID 值// getter and setter
}
本来还是想把上传文件和发送图片等写上的,后来由于需求,决定把聊天这部分交由前端直接访问环信了,所以写完发送消息就没再写了。
其实聊天这部分本人也是推荐由前端来写的。即时通信讲究的就是一个即时性,如果由后端来的话,就不能保证这个即时性了,因为如果前端走后端,后端再走环信,需要两步,而前端直接走环信,则只需要一步,就算网络等外界因素的存在,也不会太慢。而且前端也可以对消息进行一个缓存,从而降低网络访问,而且现在的IM应用也基本上都是这么来的。
以上仅供参考,如有不妥的地方请留下您的见解,谢谢。
Java服务端集成环信IM相关推荐
- Java服务端集成环信im即时通讯
Java服务端集成环信im即时通讯 先去GitHub上下载官方给的示例 说明一下环信给的demo 解压下载的包 导入项目之后加几个jar包 注意事项 修改OrgInfo和TokenUtil类 跟据IM ...
- 集成环信即时通讯(IM)及使用——服务端
文章目录 目的 流程 使用 注册账号和创建应用 调用环信接口 建议 如何自己搭建一个im服务器 目的 本篇讲述服务端如何集成环信SDK,实现IM系统. 流程 注册账号 创建应用 调用环信接口 使用 注 ...
- Java集成环信即时通讯(SpringBoot)
Java集成环信即时通讯(SpringBoot) 前言:链接: 环信官网友情链接. 官网注册一个社区版账号,社区版限制注册100个用户. 企业版资费如下 还有硬性要求,账号余额9000以上 准备完成得 ...
- 【快速搞定】教你如何快速集成环信(android端EaseUI),跳过我踩过的坑
[快速搞定]教你如何快速集成环信(android端EaseUI),跳过我踩过的坑 一.前言 最近,公司准备集成一个即时通讯的功能,于是我找到了环信,于是我就这样被坑了,于是就有了本文来教大家如何跳出我 ...
- java服务端部署到k8s集群
首先java服务端部署到k8s中是通过git提交,那要准备好各种配置 1.先创建一个空的git文件 2.java服务端的jar包放到文件中 3.提交的文件必须包含java环境(这个环境就是引入自己打包 ...
- 环信是否支持html,VUE项目集成环信WebIM即时通信以及所遇到的问题
功能背景: 以前和朋友一块儿作了一个wbe项目集成环信的即时通讯的功能,作的时候感叹web端文档太少,并且npm包有一些坑,记录下来写了这篇博客,以后不断有人加我微信问我,怎么集成.如今我再来重写一下 ...
- 环信即时通讯SDK集成——如何使用Swift快速集成环信IM iOS SDK并实现单聊
本文介绍如何使用swift快速集成环信即时通讯 IM iOS SDK 实现单聊. 前提条件 • Xcode (推荐最新版本). • 安装 iOS 10.0 或更高版本的 iOS 模拟器或 Apple ...
- 聊一聊 Java 服务端中的乱象
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:阿里巴巴中间件 查尔斯·狄更斯在<双城记>中写道 ...
- 那些年,我们见过的 Java 服务端乱象
点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 Photo by The Book Tutor @Youtube 文 | 陈昌毅 导读 查尔斯 ...
最新文章
- FFmpeg中RTSP客户端拉流测试代码
- android 视频图片,Android - 视频提取图片方法
- 第七章 ReentrantLock总结
- html页面创建二维数组,二维数组到HTML表?
- 二 Array 数组常用操作方法
- Delphi自定义过程获取本机ip,名字,用户名
- 括号生成Python解法
- 【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要流程分析
- c ++递归算法数的计数_C ++程序使用数组中的递归查找数字的最后一次出现
- C++ 对引用的理解5
- 计算机网络项目化实训教程,计算机网络项目实训教程
- HTMl文件的阶层架构 访问父元素和子元素
- HDU2027 统计元音【入门】
- 点分十进制ip地址转为十进制数字地址-python3实现
- 为什么蓝鸽的听力下载完还是听不了_听力训练方法干货-说说我与雅思听力的那些事情...
- 问题:宇视摄像机如何对接第三方录像机
- 80386 保护模式
- Android OpenGL ES(七)----理解纹理与纹理过滤
- python操作word文档
- redis入门(三)