首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

HttpClient 實戰:微信公眾號模板消息群發完整實現(獲取 Access Token + 發送接口調用)

     編輯:Java 綜合教學

業務場景與前置條件

  • 已認證的微信公眾號(服務號)或測試賬號
  • 已獲取 AppID 與 AppSecret
  • 目標:向所有關注用戶或指定 OpenID 列表群發文本/圖文/模板消息

核心流程圖解

  1. 調用 /cgi-bin/token 獲取 access_token(有效期 7200 秒,建議緩存)
  2. 構造群發消息 JSON 體(支持文本、圖文、模板等類型)
  3. 調用 /cgi-bin/message/mass/sendall 或 /cgi-bin/message/mass/send 發送
  4. 解析返回 errcode 判斷結果,處理重試與錯誤碼

Maven 依賴 (HttpClient 4.5+)

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

核心工具類:WechatApiClient

public class WechatApiClient {
    private static final Logger log = LoggerFactory.getLogger(WechatApiClient.class);
    private static final String API_BASE = "https://api.weixin.qq.com/cgi-bin";
    private static final String TOKEN_URL = API_BASE + "/token?grant_type=client_credential&appid=%s&secret=%s";
    private static final String MASS_SEND_ALL_URL = API_BASE + "/message/mass/sendall?access_token=%s";
    private static final String MASS_SEND_URL = API_BASE + "/message/mass/send?access_token=%s";

    private final CloseableHttpClient httpClient;
    private final ObjectMapper mapper = new ObjectMapper(); // 或 FastJson

    public WechatApiClient() {
        // 配置連接池、SSL 信任所有 (生產環境請配置正規憑證)
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("https", SSLConnectionSocketFactory.getSocketFactory())
            .build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        cm.setMaxTotal(20);
        cm.setDefaultMaxPerRoute(10);
        this.httpClient = HttpClients.custom().setConnectionManager(cm).build();
    }

    /** 獲取 access_token,建議外層緩存 7000 秒 */
    public String getAccessToken(String appId, String appSecret) throws IOException {
        String url = String.format(TOKEN_URL, appId, appSecret);
        HttpGet get = new HttpGet(url);
        try (CloseableHttpResponse resp = httpClient.execute(get)) {
            String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
            JsonNode node = mapper.readTree(body);
            if (node.has("errcode")) {
                throw new WechatApiException(node.get("errcode").asInt(), node.get("errmsg").asText());
            }
            return node.get("access_token").asText();
        }
    }

    /** 群發給所有關注者 */
    public String massSendAll(String accessToken, String msgType, Object content) throws IOException {
        String url = String.format(MASS_SEND_ALL_URL, accessToken);
        return executeMassSend(url, buildMassMessage(true, null, msgType, content));
    }

    /** 群發給指定 OpenID 列表 */
    public String massSendToUsers(String accessToken, List<String> openIds, String msgType, Object content) throws IOException {
        String url = String.format(MASS_SEND_URL, accessToken);
        return executeMassSend(url, buildMassMessage(false, openIds, msgType, content));
    }

    private ObjectNode buildMassMessage(boolean toAll, List<String> openIds, String msgType, Object content) {
        ObjectNode root = mapper.createObjectNode();
        if (toAll) {
            root.putObject("filter").put("is_to_all", true);
        } else {
            ObjectNode filter = root.putObject("filter");
            filter.put("is_to_all", false);
            ArrayNode arr = filter.putArray("touser");
            openIds.forEach(arr::add);
        }
        root.put("msgtype", msgType);
        root.set(msgType, mapper.valueToTree(content));
        return root;
    }

    private String executeMassSend(String url, ObjectNode payload) throws IOException {
        HttpPost post = new HttpPost(url);
        post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        post.setEntity(new StringEntity(mapper.writeValueAsString(payload), StandardCharsets.UTF_8));
        try (CloseableHttpResponse resp = httpClient.execute(post)) {
            String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
            log.info("Wechat mass send response: {}", body);
            return body;
        }
    }

    public void close() throws IOException { httpClient.close(); }
}

消息內容構造示例

1. 文本消息

// 發送給所有人
String textContent = "{\"content\":\"歡迎關注我們的公眾號!\"}";
client.massSendAll(token, "text", textContent);

// 發送給指定用戶
List<String> openIds = Arrays.asList("oUser1", "oUser2");
client.massSendToUsers(token, openIds, "text", textContent);

2. 圖文消息

String newsContent = "{\"articles\":[{\"title\":\"標題\",\"description\":\"描述\",\"url\":\"https://xxx.com\",\"picurl\":\"https://xxx.com/img.jpg\"}]}";
client.massSendAll(token, "news", newsContent);

3. 模板消息 (需模板 ID)

String tmplContent = "{\"touser\":\"OPENID\",\"template_id\":\"TMPL_ID\",\"url\":\"https://xxx.com\",\"data\":{\"first\":{\"value\":\"您好\"},\"keyword1\":{\"value\":\"訂單號\"}}}";
// 模板消息使用 /cgi-bin/message/template/send 接口,非群發接口

Controller 調用示例

@RestController
@RequestMapping("/wechat")
public class WechatController {
    @Autowired private WechatApiClient wechatClient;
    @Value("${wechat.appId}") private String appId;
    @Value("${wechat.appSecret}") private String appSecret;

    @GetMapping("/sendAllText")
    public Result sendAllText(@RequestParam String content) {
        try {
            String token = wechatClient.getAccessToken(appId, appSecret);
            String resp = wechatClient.massSendAll(token, "text", "{\"content\":\"" + content + "\"}");
            return Result.ok(resp);
        } catch (Exception e) {
            log.error("群發失敗", e);
            return Result.fail(e.getMessage());
        }
    }
}

關鍵注意事項與最佳實踐

  1. Access Token 緩存:有效期 2 小時,必須全局緩存(Redis/內存),避免頻繁調用觸發限流。
  2. 錯誤碼處理: 45015 (回覆超時)、45047 (群發頻率超限)、40003 (OpenID 非法) 等需具體業務重試或告警。
  3. SSL 配置:生產環境請使用正式 CA 憑證,勿使用 TrustAll 策略。
  4. 連接池優化:高併發場景調大 MaxTotal、MaxPerRoute,配置 RequestConfig 超時時間。
  5. 異步化:大量用戶群發建議放入消息隊列 (RabbitMQ/Kafka) 異步處理,避免阻塞 Web 線程。

總結

基於 HttpClient 4.5+ 實現微信群發核心在於:標準化 HTTP 客端配置、JSON 序列化與微信接口協議對齊、Access Token 生命週期管理、錯誤碼語義化處理。封裝為可復用 Client 組件,配合 Spring Boot 外部化配置,即可快速接入微信消息觸達能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved