首页 / 博客 / API·多语言·接口

Java 对接微信 API 完整教程(发消息+收回调)

分类:API·多语言·接口 · 标签:Java、微信API、微信机器人

前言

在企业自动化运营、客服系统、通知推送等场景中,通过程序控制微信账号收发消息是一个常见需求。与企业微信官方 API 不同,针对个人微信的自动化通常依赖第三方 HTTP 接口,开发者只需调用 REST 接口即可完成扫码登录、消息收发、联系人管理、群管理等操作。这种方式的优势在于不需要了解微信底层协议细节,所有复杂的协议封装和账号维护都由平台层承担,业务侧只关注接口调用逻辑即可。

本文以 Java 为例,完整演示如何完成以下几个核心流程:

  1. 调用接口获取二维码并完成扫码登录,拿到 appId
  2. 主动发送文本消息、图片消息
  3. 配置回调地址,在 Spring Boot 中接收并解析平台推送的消息
  4. 讲解防封频率控制与常见排错思路

文章不依赖任何特定 SDK,仅使用 HttpClient(Java 11+)和 Spring Boot,代码可直接复制并根据实际接口文档调整。整体代码风格以可读性和可维护性为优先,便于在真实项目中二次封装。


一、整体架构与准备工作

1.1 工作流概览

个人微信 HTTP API 的使用流程可以分为两个方向:

方向触发方式说明
主动调用你的代码发起 POST 请求发消息、拉取联系人、建群等
被动接收(回调)平台推送 POST 到你的服务收到消息、好友申请、退群等事件

两个方向都通过 HTTP JSON 完成,不需要维护长连接。主动调用适合定时任务、触发式通知等场景;被动回调则适合即时响应类场景,例如收到关键词后自动回复。实际项目通常两者并用——主动发送通知,同时监听用户回复。

1.2 所需环境

在本地开发阶段,如果没有公网地址,可以使用 ngrok、frp 等内网穿透工具临时暴露本地端口,待功能调通后再部署至云服务器。

1.3 基础配置类

在项目中创建一个配置常量类,统一管理接口地址和鉴权信息:

java/**
 * 微信 HTTP API 基础配置
 * BASE_URL 和 TOKEN 在注册平台后从官方文档获取
 */
public class WxApiConfig {
    public static final String BASE_URL = "https://你的接口域名";   // 注册后在官方文档获取
    public static final String TOKEN    = "你的Token";
    public static final String APP_ID   = "你的appId";             // 扫码登录后获得

    // 鉴权 header,字段名以官方文档为准
    public static final String HEADER_TOKEN_KEY = "token";
}
注意:正式项目中建议将 TOKEN 和 APP_ID 写入环境变量或配置中心(如 Nacos、Apollo),不要硬编码在源代码中,防止代码泄漏导致账号被盗用。

二、扫码登录与获取 appId

2.1 接口说明

登录流程分两步:先调接口拿到二维码图片(或 URL),用户用微信扫码后,再轮询检查登录状态。登录成功后平台会返回 appId,后续所有接口调用都要带上这个字段。

需要注意的是,appId 与登录设备绑定,同一微信号在不同设备或不同时间登录可能会得到不同的 appId。因此建议在数据库或缓存中持久化 appId,避免每次重启服务都要重新扫码。此外,微信本身有"同时在线设备数"限制,频繁重复登录可能触发风控,建议保持登录状态的持续性。

2.2 封装通用 HTTP 工具类

javaimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class WxHttpUtil {

    private static final HttpClient CLIENT = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();

    /**
     * 发送 POST JSON 请求
     * @param path  接口路径,如 /login/getLoginQrCode
     * @param body  JSON 字符串
     * @return      响应体字符串
     */
    public static String post(String path, String body) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(WxApiConfig.BASE_URL + path))
                .header("Content-Type", "application/json")
                .header(WxApiConfig.HEADER_TOKEN_KEY, WxApiConfig.TOKEN)
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .timeout(Duration.ofSeconds(15))
                .build();

        HttpResponse<String> response = CLIENT.send(request,
                HttpResponse.BodyHandlers.ofString());
        return response.body();
    }
}

2.3 获取登录二维码

javaimport com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class WxLoginService {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 请求二维码,返回二维码图片 URL 或 Base64 数据
     * 具体字段以官方文档为准
     */
    public static String getLoginQrCode() throws Exception {
        // 接口及参数以官方文档为准
        String body = "{}";
        String resp = WxHttpUtil.post("/login/getLoginQrCode", body);
        JsonNode root = MAPPER.readTree(resp);

        if (root.path("ret").asInt() != 200) {
            throw new RuntimeException("获取二维码失败: " + root.path("msg").asText());
        }
        // data 字段内容以实际文档为准
        return root.path("data").path("qrCodeUrl").asText();
    }

    /**
     * 轮询登录状态,返回 appId(登录成功后)
     * 建议每 3 秒轮询一次,最多等待 2 分钟
     */
    public static String checkLogin() throws Exception {
        String body = "{}";
        String resp = WxHttpUtil.post("/login/checkLogin", body);
        JsonNode root = MAPPER.readTree(resp);

        int ret = root.path("ret").asInt();
        if (ret == 200) {
            return root.path("data").path("appId").asText();
        }
        return null; // 尚未扫码或未确认
    }
}

2.4 完整登录流程示例

javapublic class LoginDemo {
    public static void main(String[] args) throws Exception {
        // 1. 获取二维码
        String qrUrl = WxLoginService.getLoginQrCode();
        System.out.println("请用微信扫描以下二维码:" + qrUrl);

        // 2. 轮询等待扫码确认
        String appId = null;
        for (int i = 0; i < 40; i++) {        // 最多等 120 秒
            Thread.sleep(3000);
            appId = WxLoginService.checkLogin();
            if (appId != null && !appId.isEmpty()) {
                System.out.println("登录成功,appId = " + appId);
                break;
            }
            System.out.println("等待扫码... " + (i + 1));
        }

        if (appId == null) {
            System.out.println("登录超时,请重新获取二维码");
        }
    }
}
注意appId 是设备标识,登录一次后请妥善保存,避免频繁重复登录。建议将 appId 写入数据库,服务重启时优先读取已有值,只有在账号掉线时才重新触发扫码流程。

三、主动发送消息

3.1 发送文本消息

javapublic class WxMessageService {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 发送文本消息
     * @param toWxid   接收方微信 ID(个人 wxid 或群 ID)
     * @param content  消息内容
     */
    public static boolean sendText(String toWxid, String content) throws Exception {
        // 接口路径及参数字段名以官方文档为准
        String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
            put("appId",   WxApiConfig.APP_ID);
            put("toWxid",  toWxid);
            put("content", content);
        }});

        String resp = WxHttpUtil.post("/message/postText", body);
        JsonNode root = MAPPER.readTree(resp);
        return root.path("ret").asInt() == 200;
    }

    /**
     * 发送图片消息(传图片 URL 或 Base64,以官方文档为准)
     */
    public static boolean sendImage(String toWxid, String imgUrl) throws Exception {
        String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
            put("appId",    WxApiConfig.APP_ID);
            put("toWxid",   toWxid);
            put("imgUrl",   imgUrl);  // 字段名以官方文档为准
        }});

        String resp = WxHttpUtil.post("/message/postImage", body);
        JsonNode root = MAPPER.readTree(resp);
        return root.path("ret").asInt() == 200;
    }

    /**
     * 发送文件消息,参数以官方文档为准
     */
    public static boolean sendFile(String toWxid, String fileUrl, String fileName) throws Exception {
        String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
            put("appId",    WxApiConfig.APP_ID);
            put("toWxid",   toWxid);
            put("fileUrl",  fileUrl);
            put("fileName", fileName);
        }});

        String resp = WxHttpUtil.post("/message/postFile", body);
        JsonNode root = MAPPER.readTree(resp);
        return root.path("ret").asInt() == 200;
    }
}

3.2 批量转发图片

在群发场景下,建议先上传一次图片获取资源 ID,再用转发接口批量发送,避免重复上传占用带宽:

java/**
 * 批量转发已上传的图片(先发一次,拿到 msgId 后转发)
 * 具体转发接口及参数字段以官方文档为准
 */
public static boolean forwardImage(String toWxid, String msgId) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",  WxApiConfig.APP_ID);
        put("toWxid", toWxid);
        put("msgId",  msgId);
    }});

    String resp = WxHttpUtil.post("/message/forwardImage", body);
    JsonNode root = MAPPER.readTree(resp);
    return root.path("ret").asInt() == 200;
}
实操提示:图片转发比重复上传效率高得多。实际操作时,先将图片发给自己的文件助手拿到 msgId,再批量转发给目标列表,单次上传可复用于数百条转发,显著节省带宽和接口耗时。

四、配置回调接收消息

4.1 回调机制说明

当微信账号收到消息时,平台会将消息内容以 HTTP POST JSON 的形式推送到你预先设置的回调地址。你的服务必须:

回调是整套系统的核心入口,稳定性直接影响用户体验。建议在生产环境中为回调端点配置独立的线程池,并加入监控告警,一旦回调端点出现响应超时或异常率上升,能第一时间收到通知。

4.2 设置回调地址

java/**
 * 调用接口设置消息回调地址
 * @param callbackUrl 你的服务公网地址,如 https://yourserver.com/wx/callback
 */
public static boolean setCallback(String callbackUrl) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",       WxApiConfig.APP_ID);
        put("callbackUrl", callbackUrl);  // 字段名以官方文档为准
    }});

    String resp = WxHttpUtil.post("/login/setCallback", body);
    JsonNode root = MAPPER.readTree(resp);
    return root.path("ret").asInt() == 200;
}

4.3 Spring Boot 回调接收端点

javaimport com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
@RequestMapping("/wx")
public class WxCallbackController {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    // 异步线程池,避免回调处理阻塞响应
    private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(4);

    /**
     * 接收平台推送的消息回调
     * 回调 JSON 字段以官方文档为准,示例字段供参考
     */
    @PostMapping("/callback")
    public ResponseEntity<String> callback(@RequestBody String rawBody) {
        // 立即异步处理,确保快速返回 200
        EXECUTOR.submit(() -> handleMessage(rawBody));
        return ResponseEntity.ok("success");
    }

    private void handleMessage(String rawBody) {
        try {
            JsonNode root = MAPPER.readTree(rawBody);

            // 以下字段名以官方文档为准,示例仅供参考
            String appId      = root.path("appId").asText();
            String fromWxid   = root.path("fromWxid").asText();
            String toWxid     = root.path("toWxid").asText();
            int    type       = root.path("type").asInt();
            String content    = root.path("content").asText();
            String msgId      = root.path("msgId").asText();
            long   createTime = root.path("createTime").asLong();

            System.out.printf("[回调] 来自=%s 类型=%d 内容=%s 消息ID=%s%n",
                    fromWxid, type, content, msgId);

            // 根据消息类型分发处理
            switch (type) {
                case 1:  // 文本消息(type 值以官方文档为准)
                    handleTextMessage(fromWxid, content);
                    break;
                case 3:  // 图片消息(type 值以官方文档为准)
                    handleImageMessage(fromWxid, msgId);
                    break;
                default:
                    System.out.println("暂不处理的消息类型: " + type);
            }
        } catch (Exception e) {
            System.err.println("回调处理异常: " + e.getMessage());
        }
    }

    private void handleTextMessage(String fromWxid, String content) throws Exception {
        // 示例:收到"帮助"自动回复
        if ("帮助".equals(content.trim())) {
            WxMessageService.sendText(fromWxid, "你好,我是自动回复机器人,有什么可以帮你?");
        }
    }

    private void handleImageMessage(String fromWxid, String msgId) {
        // 异步下载图片,建议加队列控制频率
        System.out.println("收到图片消息,msgId=" + msgId + ",稍后异步下载");
        // 实际下载调用 /message/downloadImage,间隔 3-10 秒
    }
}

4.4 回调不通怎么排查

问题可能原因解决方案
收不到任何回调回调地址未设置或填错重新调用 setCallback 接口确认
偶尔丢失消息响应超时(>200ms)导致平台重传改为异步处理,立即返回 200
微信离线账号未在线检查 checkOnline 接口返回状态
内网地址不可达服务在局域网必须部署公网或用内网穿透工具
主动发送的消息无回调平台设计如此正常,只有收到的消息才触发回调

五、联系人与群管理

5.1 搜索并添加好友

java/**
 * 搜索联系人(微信号/手机号),返回搜索结果
 * 建议每天搜索不超过 10-20 次,防止触发限制
 */
public static JsonNode searchContact(String keyword) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",   WxApiConfig.APP_ID);
        put("keyword", keyword);
    }});
    String resp = WxHttpUtil.post("/contacts/search", body);
    return MAPPER.readTree(resp).path("data");
}

/**
 * 添加联系人(发送好友申请)
 * 建议每 2 小时不超过 5 个,24 小时不超过 15 个
 */
public static boolean addContact(String wxid, String remark) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",  WxApiConfig.APP_ID);
        put("wxid",   wxid);
        put("remark", remark);
    }});
    String resp = WxHttpUtil.post("/contacts/addContacts", body);
    return MAPPER.readTree(resp).path("ret").asInt() == 200;
}

5.2 群管理常用接口

java/**
 * 创建群聊
 * 建议每天建群不超过 10 个,每次间隔 10 分钟以上
 */
public static String createChatroom(java.util.List<String> memberWxids) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",   WxApiConfig.APP_ID);
        put("members", memberWxids);  // 字段名以官方文档为准
    }});
    String resp = WxHttpUtil.post("/chatroom/createChatroom", body);
    JsonNode root = MAPPER.readTree(resp);
    if (root.path("ret").asInt() == 200) {
        return root.path("data").path("chatroomId").asText();
    }
    throw new RuntimeException("建群失败: " + root.path("msg").asText());
}

/**
 * 邀请成员入群
 */
public static boolean inviteMember(String chatroomId, java.util.List<String> wxids) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",      WxApiConfig.APP_ID);
        put("chatroomId", chatroomId);
        put("members",    wxids);
    }});
    String resp = WxHttpUtil.post("/chatroom/inviteMember", body);
    return MAPPER.readTree(resp).path("ret").asInt() == 200;
}

/**
 * 设置群公告
 */
public static boolean setAnnouncement(String chatroomId, String content) throws Exception {
    String body = MAPPER.writeValueAsString(new java.util.HashMap<String, Object>() {{
        put("appId",        WxApiConfig.APP_ID);
        put("chatroomId",   chatroomId);
        put("announcement", content);
    }});
    String resp = WxHttpUtil.post("/chatroom/setChatroomAnnouncement", body);
    return MAPPER.readTree(resp).path("ret").asInt() == 200;
}
实操提示:建群时至少要拉入 2 名真实好友才能成功创建群聊,纯机器人账号互拉容易触发风控。建群后建议等待 30 秒再执行后续邀请操作,避免操作过于密集被平台判定为异常行为。

六、HTTP API 接入方案选型

在自研 HTTP 调用之外,市面上也有一些提供托管接口的平台,省去自己维护协议层的工作。例如,WechatApi 提供扫码登录、消息收发、好友与群管理等 REST 接口,HTTP 调用即可,适合不想维护底层协议的团队。本文代码结构与此类平台的接口风格基本一致,需根据各自平台的官方文档调整接口路径和字段名。


七、频率控制与防封建议

频率控制是长期稳定运行的关键,下表汇总了主要操作的建议上限:

操作建议上限其他注意
搜索联系人10-20 次/天
主动添加好友5 次/2h,15 次/24h新号在线 3 天后再操作
被动通过好友≤200 次/天
建群≤10 个/天每次间隔 10 分钟以上
获取朋友圈动态≤200 次/天点赞/评论随机间隔 5-20 秒
下载文件/图片加队列,每条间隔 3-10 秒

在代码层面,推荐用 Guava 的 RateLimiter 或自定义令牌桶来控制调用频率:

javaimport com.google.common.util.concurrent.RateLimiter;

public class RateLimitedSender {
    // 每秒最多 0.5 次(即 2 秒一次)
    private static final RateLimiter LIMITER = RateLimiter.create(0.5);

    public static boolean sendTextSafe(String toWxid, String content) throws Exception {
        LIMITER.acquire();  // 阻塞直到获得令牌
        return WxMessageService.sendText(toWxid, content);
    }
}

对于批量消息场景,还应在每条消息之间加入随机延迟(500ms-3000ms),避免固定间隔被识别为机器行为:

javapublic static void batchSend(java.util.List<String> targets, String content) throws Exception {
    java.util.Random random = new java.util.Random();
    for (String wxid : targets) {
        WxMessageService.sendText(wxid, content);
        // 随机延迟 1-3 秒
        long delay = 1000 + random.nextInt(2000);
        Thread.sleep(delay);
    }
}

除频率控制外,还有几点防封经验值得注意:


八、常见问题排错

8.1 接口调用失败

ret 不是 200 时,优先检查以下几点:

  1. Token 是否正确:对比平台后台的 Token 和代码中的值
  2. appId 是否有效:调用 checkOnline 接口确认账号在线
  3. 是否触发频率限制:降低调用频率后重试
  4. 消息内容是否合规:带营销词汇的内容容易被平台拒绝

8.2 回调收不到消息

按以下顺序逐步排查:

1. 确认 setCallback 返回了 ret=200
2. 用 curl 手动 POST 一条数据到你的回调地址,确认端点正常
3. 确认服务部署在公网(非内网)
4. 查看平台推送日志(如有)
5. 检查微信账号是否仍在线(checkOnline)

8.3 序列化工具推荐

本文使用 Jackson,如果项目用 Gson 或 Fastjson,替换 ObjectMapper 相关代码即可,接口逻辑不变。引入 Jackson 的 Maven 依赖:

xml<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

总结

本文完整演示了 Java 对接个人微信 HTTP API 的核心流程:扫码登录获取 appId、封装通用 HTTP 工具类、发送文本与图片消息、在 Spring Boot 中异步接收回调,以及联系人和群管理的典型用法。代码为示例,具体接口路径、请求字段和返回字段以所使用平台的官方文档为准。

在实际部署中,有几个关键点需要重点关注:其一,appId 要持久化存储,避免频繁重复扫码登录;其二,回调端点必须快速响应(200ms 内),耗时操作一律异步处理;其三,批量操作要严格遵守频率限制,并加入随机延迟和账号状态监控。掌握这三点,就能搭建出一套在生产环境中长期稳定运行的微信自动化系统。如有接口字段或路径方面的疑问,请以所使用平台的官方文档为准。

想动手试试?

WechatApi 提供扫码登录、消息收发、好友与群管理等 REST 接口,注册后几分钟跑通。

立即免费注册查看开发文档

相关产品页

🔗 微信机器人开发(产品页)🔗 微信客服机器人(产品页)🔗 微信群管理机器人(产品页)

相关文章

微信API接口返回失败/收不到消息?完整排查清单微信 API 怎么对接?Python 发出第一条消息实战Node.js 微信机器人开发教程(发消息 + 收回调)个人微信API能力清单:消息/好友/群/朋友圈接口一览
© 2025 WechatApi · 企业级微信智能机器人接入平台
官网价格帮助文档博客
苏ICP备2024128799号 · 苏ICP备2023038368号