SpringBoot对接微信公众平台(2)--- 接收普通消息Demo

SpringBoot对接微信公众平台(2)--- 接收普通消息

  • 说明
  • 后端代码

说明

这里记录下自己学习SpringBoot对接微信公众平台的成长过程,以防止后面继续踩坑且方便以后直接使用。这里使用微信公众号的接口测试号来开发微信公众平台。这里承接自己的博客SpringBoot对接微信公众平台(1)— 配置微信公众平台测试号URL并校检这篇博客,在该博客项目的基础上增加接收普通消息的代码示例。

微信公众号-基础消息能力/接收普通消息:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html

后端代码

当用户给微信公众号发送消息时,根据官网给的文档说明,它会去调用之前你在测试号里面配置的URL接口地址,并且是以POST请求方式调用。所以你会在下面的Controller层看到2个/check,一个get的/check接口是用来测试微信公众号能否调用你的接口,一个Post的/check接口是用户给微信公众号发送消息时,会去调用该接口。这个接口才是实际环境中你要的功能接口。
在这里插入图片描述
SpringBoot项目目录如下:
在这里插入图片描述

pom.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>wechat-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>wechat-service</name>
    <description>wechat-service</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--web依赖,内嵌入tomcat,RestTempLate使用该依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--用来将string的json格式字符串转换成json对象-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
        <!--lombok依赖,用来对象省略写set、get方法-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <!--解析xml-->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--用来将string的json格式字符串转换成json对象-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.example.wechatservice.WechatServiceApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

application.yml代码:

server:
  port: 8080

wxChat:
  appID:你的测试公众号appID
  appsecret:你的测试公众号appsecret

config下的RestTemplateConfig配置代码如下:

package com.example.wechatservice.config;

import org.springframework.context.annotation.Bean;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
        return restTemplate;
    }

    // 设置超时时间
    public ClientHttpRequestFactory clientHttpRequestFactory(){
        //创建一个httpClient简单工厂
        SimpleClientHttpRequestFactory factory=new SimpleClientHttpRequestFactory();
        //设置连接超时时间,单位ms
        factory.setConnectTimeout(15000);
        //设置读取超时时间,单位ms
        factory.setReadTimeout(10000);
        return factory;
    }
}

button文件夹下的AbstractButton抽象类代码:

package com.example.wechatservice.button;

public abstract class AbstractButton {

    private String name;

    public AbstractButton(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

button文件夹下的Button类代码:

package com.example.wechatservice.button;

import java.util.List;

public class Button {

    private List<AbstractButton> button;

    public List<AbstractButton> getButton() {
        return button;
    }

    public void setButton(List<AbstractButton> button) {
        this.button = button;
    }
}

button文件夹下的ClickButton类代码:

package com.example.wechatservice.button;

public class ClickButton extends AbstractButton{
    public ClickButton(String name) {
        super(name);
        this.type = "click";
    }

    private String type;
    private String key;

    public String getType() {
        return type;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

button文件夹下的PhotoOrAlbumButton类代码:

package com.example.wechatservice.button;

public class PhotoOrAlbumButton extends AbstractButton{
    public PhotoOrAlbumButton(String name) {
        super(name);
        this.type = "pic_photo_or_album";
    }

    private String type;
    private String key;

    public String getType() {
        return type;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

button文件夹下的SubButton类代码:

package com.example.wechatservice.button;

import java.util.List;

public class SubButton extends AbstractButton{
    public SubButton(String name) {
        super(name);
    }

    private List<AbstractButton> sub_button;

    public List<AbstractButton> getSub_button() {
        return sub_button;
    }

    public void setSub_button(List<AbstractButton> sub_button) {
        this.sub_button = sub_button;
    }
}

button文件夹下的ViewButton类代码:

package com.example.wechatservice.button;

public class ViewButton extends AbstractButton{
    public ViewButton(String name,String url) {
        super(name);
        this.type = "view";
        this.url = url;
    }

    private String type;
    private String url;

    public String getUrl() {
        return url;
    }

    public String getType() {
        return type;
    }
}

WeChatController代码如下:

package com.example.wechatservice.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.wechatservice.button.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.security.MessageDigest;
import java.util.*;

@RestController
@RequestMapping(value = "/weChat")
public class WeChatController {

	@Resource
    private RestTemplate restTemplate;

    //测试内网穿透后在外网访问接口
    @GetMapping(value = "/hello")
    public String hello(){
        return "hello";
    }

    //测试微信公众平台里面的接口配置信息里面的URL能否调用成功,这种只是测试微信公众平台能否调用接口,但是不能识别它是否真的来自于微信公众平台来调用你的,所以才需要做验证
    @GetMapping(value = "/test")
    public String test(@RequestParam(value = "signature", required = false) String signature,
                       @RequestParam(value = "timestamp", required = false) String timestamp,
                       @RequestParam(value = "nonce", required = false) String nonce,
                       @RequestParam(value = "echostr", required = false) String echostr){
        System.out.println("微信测试公众平台调用我了!!!!");
        System.out.println("signature="+signature);
        System.out.println("timestamp="+timestamp);
        System.out.println("nonce="+nonce);
        System.out.println("echostr="+echostr);
        //必须原封不动将echostr返回给微信公众号,微信公众测试号才能配置成功那个URL,返回其他值都会导致配置失败
        return echostr;
    }

    //测试微信公众平台里面的接口配置信息,并验证它是否真的来自微信公众平台
    @GetMapping(value = "/check")
    public String check(@RequestParam(value = "signature", required = false) String signature,
                       @RequestParam(value = "timestamp", required = false) String timestamp,
                       @RequestParam(value = "nonce", required = false) String nonce,
                       @RequestParam(value = "echostr", required = false) String echostr){
        System.out.println("微信公众平台调用我了!!!!");
        //微信公众平台配置的token值
        String token="testToken";
        //1.将token、timestamp、nonce三个参数进行字典序排序
        List<String> list= Arrays.asList(token,timestamp,nonce);
        //排序
        Collections.sort(list);
        //2.将三个参数字符串拼接成一个字符串进行sha1加密
        StringBuilder stringBuilder=new StringBuilder();
        for(String s:list){
            stringBuilder.append(s);
        }
        //加密
        try{
            MessageDigest instance = MessageDigest.getInstance("SHA-1");
            //使用sha1进行加密获得byte数组
            byte[] digest=instance.digest(stringBuilder.toString().getBytes());
            StringBuilder sum=new StringBuilder();
            for(byte b:digest){
                sum.append(Integer.toHexString((b>>4)&15));
                sum.append(Integer.toHexString(b&15));
            }
            //3.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
            if(!StringUtils.isEmpty(signature)&&signature.equals(sum.toString())){
                System.out.println("与微信公众平台一致,确认来自微信公众平台。");
                //必须原封不动将echostr返回给微信公众号,微信公众测试号才能配置成功那个URL,返回其他值都会导致配置失败
                return echostr;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    //给微信公众号发送普通消息时(重中之重,与微信公众号进行交互全靠这个接口),微信公众号会调用配置好的URL接口,并且是以POST形式,所以这里有个POST的check方法,这里需要把get请求中的/check里面的验证是否真的来自微信公众平台的逻辑给加上,因为是demo所以没加验证
    //MsgType为text时,表示用户给微信公众号发的是文本消息
    //MsgType为image时,表示用户给微信公众号发的是图片消息
    //MsgType为event时,表示用户点击微信公众号里面的菜单
    @PostMapping(value = "/check")
    public String receiveMessage(@RequestBody String requestBody,
                        @RequestParam("signature") String signature,
                        @RequestParam("timestamp") String timestamp,
                        @RequestParam("nonce") String nonce,
                        @RequestParam("openid") String openid){
        System.out.println("用户给我发消息了!!");
        System.out.println("requestBody="+requestBody);
        System.out.println("signature="+signature);
        System.out.println("timestamp="+timestamp);
        System.out.println("nonce="+nonce);
        System.out.println("openid="+openid);
        Map<String,String> map=new HashMap<>();
        try{
            SAXReader reader=new SAXReader();
            Document document = reader.read(new ByteArrayInputStream(requestBody.getBytes("utf-8")));//读取微信传的xml字符串,注意这里要转成输入流
            Element root = document.getRootElement();//获取根元素
            List<Element> elementList = root.elements();//获取当前元素下的全部子元素
            for (Element e : elementList) {
                map.put(e.getName(), e.getText());
            }
            System.out.println(map);
        }catch (Exception e){
            e.printStackTrace();
        }
        //这里需要对不同的回复消息xml模板做不同封装,因为是demo所以就直接拿来用先看一看效果。
        String message="";
        //如果用户给公众号发送的是文本消息
        if(map.get("MsgType").equals("text")){
        	//如果用户发送的内容是111(相当于条件匹配),则返回主菜单文本消息
        	if(map.get("Content").equals("111")){
                message="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715665213</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[您好,欢迎你访问该微信公众号!]]></Content></xml>";
            }
        }else if(map.get("MsgType").equals("event")){//用户点击菜单栏菜单按钮时
        	//这里我只写了一个样例,这里肯定是要写所有点击菜单的判断的,因为是demo所以就不写多了
        	//如果用户点击了菜单2子级1,通过key来匹配,回复图文消息
            if(map.get("EventKey").equals("菜单2子级1")){
                //如果用户点击了菜单2子级1,回复图文消息
                message="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715656909</CreateTime><MsgType><![CDATA[news]]></MsgType><ArticleCount>1</ArticleCount><Articles><item><Title><![CDATA[菜单2子级1]]></Title><Description><![CDATA[点击进入页面,即可进入查询>>]]></Description><PicUrl><![CDATA[http://mmbiz.qpic.cn/sz_mmbiz_jpg/76BCY09cHYSZ60MBaHyHOJPibesjP6ibeRziabuSHZ4jYrITTlmVqFazCzlm8DdQ8QRUrfCulTNrEUyhUcydUiayuQ/0]]></PicUrl><Url><![CDATA[http://www.soso.com/]]></Url></item></Articles></xml>";
            }
        }else if(map.get("MsgType").equals("image")){//用户发送的是图片消息
        	//回复图片消息示例,可以先给微信公众平台发送图片消息,可以在后端控制台获取到图片的MediaId值和PicUrl值
        	message="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715665213</CreateTime><MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[A3UqbFuQxw2gVlIk1_JGw57YlBrmPA4-stcjyk8yA1MBB7pgGSgD_UxbmfwswSN4]]></MediaId></Image></xml>";
        }
        //回复文本消息示例
        //String messageText="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715665213</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[你好]]></Content></xml>";
        //回复图片消息示例,可以先给微信公众平台发送图片消息,可以在后端控制台获取到图片的MediaId值和PicUrl值
        //String messageImage="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715665213</CreateTime><MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[A3UqbFuQxw2gVlIk1_JGw57YlBrmPA4-stcjyk8yA1MBB7pgGSgD_UxbmfwswSN4]]></MediaId></Image></xml>";
        //回复语音消息示例,可以先给微信公众平台发送语音消息,可以在后端控制台获取到它的MediaId值
        //String messageVoice="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715656909</CreateTime><MsgType><![CDATA[voice]]></MsgType><Voice><MediaId><![CDATA[d15tL6-BeVLR_v1sijEi8K5nv17RDdz1jDN1DhHVnstKFGbqB8n3riY_d3e2OJlL]]></MediaId></Voice></xml>";
        //回复视频消息示例,可以先给微信公众平台发送语音消息,可以在后端控制台获取到它的MediaId值,有点问题,回复不了,需要查找原因
        //String messageVideo="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715656909</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[d15tL6-BeVLR_v1sijEi8HHaaJYRI5baAHJNz-Rsy9nhyAocMVwbdz-uFwPIzpGs2uwQC6BbBpcoK-dpNJ4QEA]]></MediaId><Title><![CDATA[test title]]></Title><Description><![CDATA[test description]]></Description></Video></xml>";
        //回复图文消息示例
        //String messageNews="<xml><ToUserName><![CDATA[ooiyP6HjMn7v42TtTkeZtj89OLKc]]></ToUserName><FromUserName><![CDATA[gh_7fe728a71cd6]]></FromUserName><CreateTime>1715656909</CreateTime><MsgType><![CDATA[news]]></MsgType><ArticleCount>1</ArticleCount><Articles><item><Title><![CDATA[我是标题]]></Title><Description><![CDATA[测试描述]]></Description><PicUrl><![CDATA[http://mmbiz.qpic.cn/sz_mmbiz_jpg/76BCY09cHYSZ60MBaHyHOJPibesjP6ibeRziabuSHZ4jYrITTlmVqFazCzlm8DdQ8QRUrfCulTNrEUyhUcydUiayuQ/0]]></PicUrl><Url><![CDATA[http://www.soso.com/]]></Url></item></Articles></xml>";
        return message;
    }

	//获取公众号的全局唯一接口调用凭据access_token,失效时间120分钟
    @GetMapping(value = "/getAccessToken")
    public String getAccessToken(){
        // 请求地址
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";
        //提交参数设置,这里map里面的key要与请求地址里面占位符名一样,{username}对应map的key:username
        Map<String, Object> map = new HashMap<>();
        map.put("appid", "你的测试公众号appID");
        map.put("secret", "你的测试公众号appsecret");
        //请求接口
        JSONObject result = restTemplate.getForObject(url, JSONObject.class,map);
        //80_Zd7nT4s07RA6gAu4F5kyM8xlpZvPijpnT4CJQr687DXdUcuVRR9SvrE4xGBtilov5sBC_2hnYQlNs34qINDSgcTLL4tdGEi1usXMEJMY-pxYS1wbLZQm7KK1s-0KFDfAFABWU
        System.out.println(result.get("access_token"));
        return result.get("access_token").toString();
    }

    //创建菜单
    @GetMapping(value = "/createMenu")
    public String createMenu(){
        //创建一级菜单
        Button button=new Button();
        List<AbstractButton> buttons=new ArrayList<>();
        //一级菜单中的第一个菜单按钮(二级菜单)
        SubButton subButton1=new SubButton("菜单1");
        List<AbstractButton> subButtons1=new ArrayList<>();
        //二级菜单菜单1的第一个按钮
        ClickButton clickButton1=new ClickButton("菜单1子级1");
        clickButton1.setKey("菜单1子级1");
        subButtons1.add(clickButton1);
        //二级菜单菜单1的第二个按钮
        ClickButton clickButton2=new ClickButton("菜单1子级2");
        clickButton2.setKey("菜单1子级2");
        subButtons1.add(clickButton2);
        subButton1.setSub_button(subButtons1);

        //一级菜单中的第二个菜单按钮(二级菜单)
        SubButton subButton2=new SubButton("菜单2");
        List<AbstractButton> subButtons2=new ArrayList<>();
        //二级菜单菜单2的第一个按钮
        ClickButton clickButton3=new ClickButton("菜单2子级1");
        clickButton3.setKey("菜单2子级1");
        subButtons2.add(clickButton3);
        //二级菜单菜单2的第二个按钮
        ClickButton clickButton4=new ClickButton("菜单2子级2");
        clickButton4.setKey("菜单2子级2");
        subButtons2.add(clickButton4);
        //二级菜单菜单2的第三个按钮
        ClickButton clickButton5=new ClickButton("菜单2子级3");
        clickButton5.setKey("菜单2子级3");
        subButtons2.add(clickButton5);
        //二级菜单菜单2的第四个按钮
        ClickButton clickButton6=new ClickButton("菜单2子级4");
        clickButton6.setKey("菜单2子级4");
        subButtons2.add(clickButton6);
        //二级菜单菜单2的第五个按钮
        ClickButton clickButton7=new ClickButton("菜单2子级5");
        clickButton7.setKey("菜单2子级5");
        subButtons2.add(clickButton7);
        subButton2.setSub_button(subButtons2);

        //一级菜单中的第三个菜单按钮(二级菜单)
        SubButton subButton3=new SubButton("菜单3");
        List<AbstractButton> subButtons3=new ArrayList<>();
        //二级菜单菜单3的第一个按钮
        ClickButton clickButton8=new ClickButton("菜单3子级1");
        clickButton8.setKey("菜单3子级1");
        subButtons3.add(clickButton8);
        //二级菜单菜单3的第二个按钮
        ClickButton clickButton9=new ClickButton("菜单3子级2");
        clickButton9.setKey("菜单3子级2");
        subButtons3.add(clickButton9);
        //二级菜单菜单3的第三个按钮
        ClickButton clickButton10=new ClickButton("菜单3子级3");
        clickButton10.setKey("菜单3子级3");
        subButtons3.add(clickButton10);
        subButton3.setSub_button(subButtons3);
        //把一级菜单中的三个按钮添加进集合
        buttons.add(subButton1);
        buttons.add(subButton2);
        buttons.add(subButton3);
        //把集合添加到一级菜单中
        button.setButton(buttons);
        System.out.println("菜单数据="+JSONObject.toJSONString(button));

        // 请求地址,这里我是先用Postman调用了getAccessToken接口获得了access_token值,在这里直接写死了供测试使用。
        String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=80_Zd7nT4s07RA6gAu4F5kyM8xlpZvPijpnT4CJQr687DXdUcuVRR9SvrE4xGBtilov5sBC_2hnYQlNs34qINDSgcTLL4tdGEi1usXMEJMY-pxYS1wbLZQm7KK1s-0KFDfAFABWU";
        // 请求头设置,指定数据以application/json格式的数据格式的数据传递参数
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/json;charset=UTF-8"));
        headers.add("Accept",MediaType.APPLICATION_JSON.toString());
        headers.add("Accept-Charset","UTF-8");

        // 组装请求体
        HttpEntity<Button> request = new HttpEntity<>(button, headers);

        // 发送post请求,并打印结果,以String类型接收响应结果JSON字符串
        //第1个参数:请求的url地址
        //第2个参数:请求的字段参数加数据格式
        //第3个参数:返回的结果类型,这里String.class表示返回结果是一个字符串。
        JSONObject result = restTemplate.postForObject(url, request,JSONObject.class);
        System.out.println(result);
        return result.toString();
    }
}

如果微信公众号测试号你本地配置好了,外网也可以访问了,在该项目代码里面,操作如下:

  1. 修改getAccessToken接口里面的appid值和secret值,改为你自己的。
  2. 使用Postman调用getAccessToken接口获得access_token值
  3. 将获取到的access_token写到创建菜单接口/createMenu里面的那个调用微信公众号菜单的接口里面
  4. 使用Postman调用菜单接口,然后在微信公众号接口测试号里面扫描那个测试号二维码,就能看到你创建的菜单
  5. 给你自己刚创建的测试公众号发送文本消息或者点击菜单,它都会去调用你在测试号里面配置的那个接口地址,并且是以Post形式,这里我的是Post方法/check这个方法,里面有和微信公众号交互的逻辑,根据用户发送的消息类型来进行匹配并回复相应消息。

这里没有实际效果展示,所以对于第一次学习微信公众号的人来说不好理解,不过你把上面代码全部复制到你的项目里面并进行测试,看控制台打印消息就能理解了,然后就是多看官网文档了,以上全部代码就是这些了。希望对各位小伙伴有帮助吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/726167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图像超分辨率重建

一、什么是图像超分辨 图像超分辨是一种技术&#xff0c;旨在通过硬件或软件的方法提高原有图像的分辨率。这一过程涉及从一系列低分辨率的图像中获取一幅高分辨率的图像&#xff0c;实现了时间分辨率向空间分辨率的转换。超分辨率重建的核心思想是利用多帧图像序列的时间带宽来…

小程序使用经纬度通过腾讯位置服务、小程序jdk、逆地址解析(位置描述)获取到详细信息

小程序后台 注册账户 控制台新建应用 配额 配额 下载jdk 下载 逆地址解析&#xff08;位置描述&#xff09; const QQMapWX require(../../libs/qqmap-wx-jssdk.min.js);getPosition() {console.log(getPosition);const that thisconst qqmapsdk new QQMapWX({key: JRKBZ…

[Linux] Shell

chsh不是一种sh,而是一个命令行使用程序&#xff0c;用于更改默认shell CentOS是个开源软件&#xff0c;没有sh,sh是商业版的&#xff0c; 按ls /bin/*sh显示的sh实际上是个链接文件&#xff0c;连接的bash 在命令行输入新的sh名&#xff0c;会启动一个新的进程&#xff0c; 输…

IDEA2023中使用run Dashboard面板?实现批量运行微服务

1、直接点击Add service--->Run Configuration Type---->Spring Boot 2、这样就出现了run Dashboard面板&#xff0c;可同时运行多个工程模块&#xff0c;shift选中所有启动类组命名&#xff08;Group Configurations&#xff09; 3、启动所有的项目

[机器学习算法]支持向量机

支持向量机&#xff08;SVM&#xff09;是一种用于分类和回归分析的监督学习模型。SVM通过找到一个超平面来将数据点分开&#xff0c;从而实现分类。 1. 理解基本概念和理论&#xff1a; 超平面&#xff08;Hyperplane&#xff09;&#xff1a;在高维空间中&#xff0c;将数据…

Transformer与强化学习结合提升物联网智能决策

在数字化时代&#xff0c;物联网(IoT)的兴起已经彻底改变了我们与物理世界的互动方式。通过将日常家居用品到精密的工业机械等设备连接到互联网&#xff0c;IoT构建了一个庞大的互联生态系统&#xff0c;它所产生的数据量是前所未有的。这些数据为我们提供了丰富的信息资源&…

am62x芯片安全类型确认(HS-SE, HS-FS or GP)

文章目录 芯片安全类型设置启动方式获取串口信息下载脚本运行脚本示例sk-am62x板卡参考芯片安全类型 AM62x 芯片有三个安全级别。 • GP:通用版本 • HS-FS:高安全性 - 现场安全型 • HS-SE:高安全性 - 强制安全型 在SD卡启动文件中,可以查看到, 但板上的芯片,到底是那…

RPM命令和YUM命令

目录 一、RPM软件包 1.1、RPM概述 1.2、查询已安装的rpm软件信息 1.3、查询未安装的 RPM 软件包文件中信息 1.4、安装、升级、卸载 RPM 软件包 二、YUM常规命令 三、手动配置Apache&#xff08;http&#xff09;服务 3.1、前提条件 3.2、开始配置 3.3、开启验证服务 …

2024人工智能指数报告(二):技术性能

背景 从2017年开始&#xff0c;斯坦福大学人工智能研究所&#xff08;HAI&#xff09;每年都会发布一份人工智能的研究报告&#xff0c;人工智能指数报告&#xff08;AII&#xff09;&#xff0c;对上一年人工智能相关的数据进行跟踪、整理、提炼并进行可视化。这份指数报告被认…

产品经理方法论

1、用户体验 5 要素 1&#xff0c;表现层是你拿到一个产品以后&#xff0c;视觉表现&#xff0c;配色&#xff0c;布局&#xff0c;排版等等 2&#xff0c;框架层&#xff0c;是交互层面的东西&#xff0c;比如&#xff0c;操作情况&#xff0c;刷新&#xff0c;页面跳转&…

双通道-程控绝缘测试电阻箱的性能

双通道-程控绝缘测试电阻箱是高精度、高性能的电气测量设备&#xff0c;广泛应用于电力系统、电气设备、电子设备等领域。采用先进的数字式电阻测量技术&#xff0c;具有高精度、高稳定性的测量性能。其测量误差小于0.05%&#xff0c;能够满足各种精密测量的需求。 双通道-程控…

EarMaster Pro中文版安装包下载及安装教程

​众所周知软件功能和优势&#xff1a;插上麦克风&#xff0c;演唱&#xff0c;拍手, 或在电脑屏幕上演奏您的答案(您还能够选择在mid键盘上演奏答案)。很明显来自丹麦皇家歌曲学院的多媒体歌曲教育软件 EarMaster Pro以问答的交互形式&#xff0c;寓教于乐的视听方法&#xff…

VMware RedHat虚拟机磁盘扩容(添加磁盘和扩展磁盘)

前言 自己的电脑上配一个虚拟机还是很有必要的&#xff0c;用起来比双系统方便一点&#xff0c;之前搞了100g的ubuntu没用到&#xff0c;后面重装redhat觉得随便搞个20g就够用了&#xff0c;后面用到之后就遇到磁盘不够用的情况&#xff0c;只能说情况允许的话&#xff0c;磁盘…

【数据结构】第十八弹---C语言实现堆排序

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、堆排序 1.1、基本思想 1.2、初步代码实现 1.3、代码优化 1.4、代码测试 总结 1、堆排序 在博主数据结构第十二弹---堆的应用有详细讲解堆…

Hadoop 2.0 大家族(一)

目录 一、Hadoop 2.0大家族概述&#xff08;一&#xff09;分布式组件&#xff08;二&#xff09;部署概述 二、ZooKeeper&#xff08;一&#xff09;ZooKeeper简介&#xff08;二&#xff09;ZooKeeper 入门 一、Hadoop 2.0大家族概述 &#xff08;一&#xff09;分布式组件 …

Java中的While循环及其示例

Java中的While循环及其示例 在本教程中&#xff0c;您将借助示例在java中学习while循环。与for循环类似&#xff0c;while循环用于重复执行一组语句&#xff0c;直到指定的条件返回false。 while循环的语法 while(condition) {statement(s); //block of code } while循环的…

RAG优化技巧|7大挑战与解決方式|提高你的LLM能力

在当今快速发展的人工智能领域&#xff0c;大型语言模型&#xff08;LLM&#xff09;已经成为无处不在的技术&#xff0c;它们不仅改变了我们与机器交流的方式&#xff0c;还在各行各业中发挥着革命性的影响。 然而&#xff0c;尽管LLM RAG的能力已经让人惊叹&#xff0c;但我…

Salia PLCC cPH2 远程命令执行漏洞(CVE-2023-46359)

漏洞描述 Salia PLCC cPH2 v1.87.0 及更早版本中存在一个操作系统命令注入漏洞&#xff0c;该漏洞可能允许未经身份验证的远程攻击者通过传递给连接检查功能的特制参数在系统上执行任意命令。 产品界面 fofa语法 "Salia PLCC" POC GET /connectioncheck.php?ip1…

考研计组chap2数据的表示和运算

3一、进位计数制 1.r进制 第i位表示r进制的权为i 2.进制转换 &#xff08;1&#xff09;r->10 对应位置数*权值 &#xff08;2&#xff09;2 -> 16 or 8 每三位2进制数可表示1位16进制 每四位2进制数可表示1位16进制 so 分开之后转为16进制即可 eg&#xff1a;1…

iOS APP内存泄漏的问题

iOS APP内存泄漏是指应用程序不再使用内存&#xff0c;但内存却没有被释放&#xff0c;导致应用程序占用过多的内存&#xff0c;甚至崩溃。内存泄漏是iOS开发中常见的问题&#xff0c;会严重影响应用程序的性能和稳定性。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…