杰瑞科技汇

Java如何实现微信公众平台自定义菜单?

微信公众平台的自定义菜单创建是一个典型的 RESTful API 调用过程,核心步骤是:

Java如何实现微信公众平台自定义菜单?-图1
(图片来源网络,侵删)
  1. 获取 Access Token:调用所有接口的凭证。
  2. 构建菜单数据:按照微信规定的 JSON 格式,组装你的菜单结构。
  3. 发送 HTTP POST 请求:将菜单数据发送到微信提供的接口地址。

下面我们分步讲解,并提供完整的 Java 代码示例。


第一步:准备工作

在开始写代码之前,你需要在微信公众平台后台完成以下配置:

  1. 获取 AppID 和 AppSecret

    • 登录 微信公众平台
    • 进入 “开发” -> “基本配置” 页面。
    • 你可以在这里找到你的 AppIDAppSecret,这两个是调用 API 的关键。
  2. 服务器 IP 白名单

    Java如何实现微信公众平台自定义菜单?-图2
    (图片来源网络,侵删)
    • 在 “基本配置” 页面,找到 “开发者权限” -> “服务器 IP 白名单”。
    • 将你的 Java 应用程序所在的服务器 IP 地址添加到白名单中,这是为了安全,只有白名单内的服务器才能调用 API。

第二步:核心逻辑与代码实现

我们将使用 Java 和流行的 OkHttp 库来发送 HTTP 请求,以及 Gson 库来处理 JSON 数据。

添加 Maven 依赖

在你的 pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- OkHttp: 用于发送 HTTP 请求 -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version> <!-- 使用较新稳定版本 -->
    </dependency>
    <!-- Gson: 用于处理 JSON -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version> <!-- 使用较新稳定版本 -->
    </dependency>
</dependencies>

创建菜单数据模型

我们需要创建 Java 类来与微信的 JSON 菜单数据结构对应,一个典型的菜单结构包含 button 数组,每个 button 可以是 click(点击事件)、view(跳转链接)或 sub_button(子菜单)。

// WxMenu.java - 菜单的根对象
import java.util.List;
public class WxMenu {
    private List<Button> button;
    // Getters and Setters
    public List<Button> getButton() {
        return button;
    }
    public void setButton(List<Button> button) {
        this.button = button;
    }
}
// Button.java - 菜单按钮
public class Button {
    private String name; // 按钮名称
    private String type; // 按钮类型: click, view, miniprogram 等
    private String key; // click 事件key
    private String url; // view 链接
    private String appid; // 小程序 appid
    private String pagepath; // 小程序页面路径
    private List<Button> sub_button; // 子按钮
    // Getters and Setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getType() { return type; }
    public void setType(String type) { this.type = type; }
    public String getKey() { return key; }
    public void setKey(String key) { this.key = key; }
    public String getUrl() { return url; }
    public void setUrl(String url) { this.url = url; }
    public String getAppid() { return appid; }
    public void setAppid(String appid) { this.appid = appid; }
    public String getPagepath() { return pagepath; }
    public void setPagepath(String pagepath) { this.pagepath = pagepath; }
    public List<Button> getSub_button() { return sub_button; }
    public void setSub_button(List<Button> sub_button) { this.sub_button = sub_button; }
}

编写 API 调用工具类

这个工具类将包含获取 Access Token 和创建菜单的核心方法。

Java如何实现微信公众平台自定义菜单?-图3
(图片来源网络,侵删)
import com.google.gson.Gson;
import okhttp3.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class WeChatApiUtil {
    // 替换成你自己的 AppID 和 AppSecret
    private static final String APP_ID = "你的AppID";
    private static final String APP_SECRET = "你的AppSecret";
    // 微信 API 地址
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    private static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s";
    private static final OkHttpClient client = new OkHttpClient();
    private static final Gson gson = new Gson();
    /**
     * 获取 Access Token
     * @return Access Token 字符串
     * @throws IOException 如果请求失败
     */
    public static String getAccessToken() throws IOException {
        String url = String.format(ACCESS_TOKEN_URL, APP_ID, APP_SECRET);
        Request request = new Request.Builder().url(url).build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response + " - body: " + response.body().string());
            }
            String responseBody = response.body().string();
            // 使用 Gson 解析 JSON,提取 access_token
            AccessTokenResponse tokenResponse = gson.fromJson(responseBody, AccessTokenResponse.class);
            if (tokenResponse.getAccessToken() == null) {
                throw new IOException("Failed to get access token: " + tokenResponse.getErrmsg());
            }
            return tokenResponse.getAccessToken();
        }
    }
    /**
     * 创建自定义菜单
     * @param menu 菜单对象
     * @param accessToken Access Token
     * @throws IOException 如果请求失败
     */
    public static void createMenu(WxMenu menu, String accessToken) throws IOException {
        String url = String.format(CREATE_MENU_URL, accessToken);
        String json = gson.toJson(menu);
        RequestBody body = RequestBody.create(json, MediaType.get("application/json; charset=utf-8"));
        Request request = new Request.Builder().url(url).post(body).build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response + " - body: " + response.body().string());
            }
            String result = response.body().string();
            // 检查微信返回的 JSON 状态码
            WxApiResponse apiResponse = gson.fromJson(result, WxApiResponse.class);
            if (apiResponse.getErrcode() != 0) {
                throw new IOException("WeChat API Error: " + apiResponse.getErrcode() + " - " + apiResponse.getErrmsg());
            }
            System.out.println("菜单创建成功!");
        }
    }
    // 用于解析获取 Access Token 返回的 JSON
    private static class AccessTokenResponse {
        private String access_token;
        private int expires_in;
        private String errmsg;
        private int errcode;
        public String getAccessToken() { return access_token; }
        public int getExpiresIn() { return expires_in; }
        public String getErrmsg() { return errmsg; }
        public int getErrcode() { return errcode; }
    }
    // 用于解析微信 API 通用返回的 JSON
    private static class WxApiResponse {
        private int errcode;
        private String errmsg;
        public int getErrcode() { return errcode; }
        public String getErrmsg() { return errmsg; }
    }
    public static void main(String[] args) {
        try {
            // 1. 获取 Access Token
            String accessToken = getAccessToken();
            System.out.println("Access Token: " + accessToken.substring(0, 20) + "..."); // 出于安全考虑,只打印部分
            // 2. 构建菜单
            WxMenu menu = new WxMenu();
            List<Button> buttons = new ArrayList<>();
            // 第一个按钮:点击事件
            Button clickButton = new Button();
            clickButton.setName("今日歌曲");
            clickButton.setType("click");
            clickButton.setKey("V1001_TODAY_MUSIC");
            buttons.add(clickButton);
            // 第二个按钮:跳转链接
            Button viewButton = new Button();
            viewButton.setName("搜索");
            viewButton.setType("view");
            viewButton.setUrl("https://www.soso.com/");
            buttons.add(viewButton);
            // 第三个按钮:子菜单
            Button subButton = new Button();
            subButton.setName("菜单");
            subButton.setType("null"); // 标识为父按钮
            List<Button> subButtons = new ArrayList<>();
            Button subButton1 = new Button();
            subButton1.setName("二级菜单1");
            subButton1.setType("click");
            subButton1.setKey("V1001_GOOD");
            subButtons.add(subButton1);
            Button subButton2 = new Button();
            subButton2.setName("二级菜单2");
            subButton2.setType("view");
            subButton2.setUrl("https://www.soso.com/");
            subButtons.add(subButton2);
            subButton.setSub_button(subButtons);
            buttons.add(subButton);
            menu.setButton(buttons);
            // 3. 发送请求创建菜单
            createMenu(menu, accessToken);
        } catch (IOException e) {
            System.err.println("发生错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

第三步:代码解释

  1. WeChatApiUtil

    • APP_IDAPP_SECRET:请务必替换成你自己的。
    • OkHttpClientOkHttp 的核心,用于执行网络请求。
    • GsonGoogle 的 JSON 库,用于将 Java 对象序列化为 JSON 字符串,以及将 JSON 字符串反序列化为 Java 对象。
  2. getAccessToken() 方法

    • 拼接获取 access_token 的 URL。
    • 使用 OkHttp 发送一个 GET 请求。
    • 将返回的 JSON 响应体解析为 AccessTokenResponse 对象,并提取 access_token
    • 注意access_token 有 2 小时的有效期,建议你缓存起来,过期后再重新获取,不要每次请求都调用此方法。
  3. createMenu() 方法

    • 拼接创建菜单的 URL,并将 access_token 作为参数。
    • 使用 GsonWxMenu Java 对象转换为 JSON 字符串。
    • 创建一个 POST 请求,并将 JSON 字符串作为请求体(RequestBody)。
    • 发送请求并处理响应,微信会返回一个 JSON,其中包含 errcode(错误码)和 errmsg(错误信息)。errcode 为 0,表示成功。
  4. main() 方法

    • 这是程序的入口,演示了如何使用上述工具。
    • 构建菜单:我们创建了一个包含三个按钮的菜单。
      • "今日歌曲"typeclick,用户点击后,微信服务器会推送一条 click 事件给你的服务器,你需要处理这个事件。key 用于唯一标识这个按钮。
      • "搜索"typeview,用户点击后会直接跳转到 https://www.soso.com/
      • "菜单"typenull(或不设置 type),它作为一个父按钮,下面包含两个子按钮,子按钮可以是 clickview 类型。

第四步:其他重要操作

除了创建菜单,你可能还需要查询和删除菜单。

查询菜单

public static String getMenu(String accessToken) throws IOException {
    String url = String.format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token=%s", accessToken);
    Request request = new Request.Builder().url(url).build();
    try (Response response = client.newCall(request).execute()) {
        if (!response.isSuccessful()) {
            throw new IOException("Unexpected code " + response);
        }
        return response.body().string();
    }
}

删除菜单

public static void deleteMenu(String accessToken) throws IOException {
    String url = String.format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s", accessToken);
    Request request = new Request.Builder().url(url).build();
    try (Response response = client.newCall(request).execute()) {
        if (!response.isSuccessful()) {
            throw new IOException("Unexpected code " + response);
        }
        String result = response.body().string();
        WxApiResponse apiResponse = gson.fromJson(result, WxApiResponse.class);
        if (apiResponse.getErrcode() != 0) {
            throw new IOException("WeChat API Error: " + apiResponse.getErrcode() + " - " + apiResponse.getErrmsg());
        }
        System.out.println("菜单删除成功!");
    }
}

注意事项

  • Access Token 缓存access_token 有效期 2 小时,一定要做好缓存,避免频繁请求导致接口调用次数超限或性能问题。
  • 按钮数量限制:菜单栏最多只能有 3 个一级按钮,每个一级按钮下最多可以有 5 个二级按钮。
  • 按钮名称长度:按钮名称长度不能超过 16 个汉字(或 8 个英文字母),子菜单按钮名称长度不能超过 60 个汉字(或 30 个英文字母)。
  • HTTPS:所有与微信服务器的通信都必须使用 HTTPS 协议。
  • 错误处理:代码中已经包含了基本的错误处理,但在生产环境中,你需要更健壮的异常处理和日志记录。

通过以上步骤和代码,你就可以成功地在 Java 应用中为你的微信公众号创建自定义菜单了。

分享:
扫描分享到社交APP
上一篇
下一篇