首页
归档
留言
广告合作
友链
美女主播
Search
1
博瑞GE车机升级/降级
5,146 阅读
2
Mac打印机设置黑白打印
4,517 阅读
3
修改elementUI中el-table树形结构图标
4,516 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,351 阅读
5
intelliJ Idea 2022.2.X破解
4,060 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
mybatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
Java Script
asp.net
微信小程序
Sentinel
UniApp
MySQL
asp.net core
IntelliJ IDEA
Jpa
树莓派
Laughing
累计撰写
570
篇文章
累计收到
1,424
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
广告合作
友链
美女主播
搜索到
3
篇与
的结果
2022-11-15
解决小程序报错the api need to be declared in the requiredPrivateInfos field in app.json
一、报错信息从报错信息可以大概看到,应该是缺少授权导致的。PS:这里真的想吐槽一下微信的接口,真的是三天两头的变,发个版就得调整一次,心累。二、微信官方文档说明打开微信开发文档查看配置信息。为了开发者能够正常使用获取模糊地理位置等接口,以及后续对于代码提审环节的优化,自 **2022 年 7 月 14 日起,开发者在使用地理位置相关接口时(共计 8 个),需要提前在 app.json 中进行配置。在代码中使用的地理位置相关接口(共计 8 个),开发者均需要在 app.json 中 requiredPrivateInfos 配置项中声明,代码格式如下:地理位置接口新增与相关流程调整 | 微信开放社区三、解决方法因为我是用uni-app框架开发的,所以是在manifest.json里面配置,如果是用其他框架开发的话,一般都是在app.json里面配置,不过原理都是一样的。
2022年11月15日
1,805 阅读
0 评论
7 点赞
2021-05-02
微信公众号开发之回复用户留言
在微信公众号开发之公众号基础配置一文中,我们介绍了如何对微信公众号进行基础配置。下面基于李森的博客的一个需求说明一下如何实现公众号用户的回复开发。需求描述用户给公众号发送消息时,我们查询博客的内容,然后回复给用户。代码实现其实用户回复的请求,跟微信公众号开发之公众号基础配置中配置的URL是一致的。区别在于我们在微信公众号开发之公众号基础配置中配置的请求是GET请求,用户回复的时候,微信会通过POST请求到后台。定义Controller相应微信请求POST请求定义其实没啥特别的,代码如下 @PostMapping("official") public void post(HttpServletRequest request, HttpServletResponse response) { try { request.setCharacterEncoding("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } response.setCharacterEncoding("UTF-8"); // 调用核心业务类接收消息、处理消息 // String respMessage = weixinPost(request); String respMessage = messageService.newMessageRequest(request); // 响应消息 PrintWriter out = null; try { out = response.getWriter(); out.print(respMessage); } catch (IOException e) { e.printStackTrace(); logger.error(e.getMessage()); } finally { out.close(); out = null; } }封装实体封装消息基础实体BaseMessage.java/** * 微信自动回复消息封装 */ @Data public class BaseMessage { // 开发者微信号 private String ToUserName; // 发送方帐号(一个OpenID) private String FromUserName; // 消息创建时间 (整型) private long CreateTime; // 消息类型(text/image/location/link) private String MsgType; // 消息id,64位整型 private long MsgId; /** * 位0x0001被标志时,星标刚收到的消息 */ private int FuncFlag; }封装普通文本消息实体TextMessage.java@EqualsAndHashCode(callSuper = true) @Data public class TextMessage extends BaseMessage{ // 消息内容 private String Content; }封装图文消息实体Article.java@Data public class Article { /** * 图文消息描述 */ private String Description; /** * 图片链接,支持JPG、PNG格式,<br> * 较好的效果为大图640*320,小图80*80 */ private String PicUrl; /** * 图文消息名称 */ private String Title; /** * 点击图文消息跳转链接 */ private String Url; }封装多条图文消息实体'NewsMessage.java'@EqualsAndHashCode(callSuper = true) @Data public class NewsMessage extends BaseMessage{ /** * 图文消息个数,限制为10条以内 */ private Integer ArticleCount; /** * 多条图文消息信息,默认第一个item为大图 */ private List<Article> Articles; }封装工具类我们需要将实体转换成xml结构,微信消息都是通过xml格式进行数据交互的。public class MessageUtil { /** * 返回消息类型:文本 */ public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * 返回消息类型:音乐 */ public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; /** * 返回消息类型:图文 */ public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * 请求消息类型:文本 */ public static final String REQ_MESSAGE_TYPE_TEXT = "text"; /** * 请求消息类型:图片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * 请求消息类型:链接 */ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * 请求消息类型:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * 请求消息类型:音频 */ public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; /** * 请求消息类型:推送 */ public static final String REQ_MESSAGE_TYPE_EVENT = "event"; /** * 事件类型:subscribe(订阅) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; /** * 事件类型:unsubscribe(取消订阅) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; /** * 事件类型:CLICK(自定义菜单点击事件) */ public static final String EVENT_TYPE_CLICK = "CLICK"; /** * xml转换为map * * @param request * @return * @throws IOException */ public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException { Map<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); InputStream ins = null; try { ins = request.getInputStream(); } catch (IOException e1) { e1.printStackTrace(); } Document doc = null; try { doc = reader.read(ins); Element root = doc.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName(), e.getText()); } return map; } catch (DocumentException e1) { e1.printStackTrace(); } finally { ins.close(); } return null; } /** * @param @param request * @param @return * @param @throws Exception * @Description: 解析微信发来的请求(XML) */ public static Map<String, String> parseXml(HttpServletRequest request) throws Exception { // 将解析结果存储在HashMap中 Map<String, String> map = new HashMap<String, String>(); // 从request中取得输入流 InputStream inputStream = request.getInputStream(); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子节点 List<Element> elementList = root.elements(); // 遍历所有子节点 for (Element e : elementList) map.put(e.getName(), e.getText()); // 释放资源 inputStream.close(); inputStream = null; return map; } // public static XStream xstream = new XStream(); /** * 文本消息对象转换成xml * * @param textMessage 文本消息对象 * @return xml */ public static String textMessageToXml(TextMessage textMessage) { // XStream xstream = new XStream(); xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } /** * @param @param newsMessage * @param @return * @Description: 图文消息对象转换成xml */ public static String newsMessageToXml(NewsMessage newsMessage) { xstream.alias("xml", newsMessage.getClass()); xstream.alias("item", new Article().getClass()); return xstream.toXML(newsMessage); } /** * 对象到xml的处理 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); }封装服务层 /** * 微信公众号处理 * * @param request * @return */ @Override public String newMessageRequest(HttpServletRequest request) { String respMessage = null; try { // xml请求解析 Map<String, String> requestMap = MessageUtil.xmlToMap(request); // 发送方帐号(open_id) String fromUserName = requestMap.get("FromUserName"); // 公众帐号 String toUserName = requestMap.get("ToUserName"); // 消息类型 String msgType = requestMap.get("MsgType"); // 用户发送的消息消息内容 String content = requestMap.get("Content"); logger.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType + ",content:" + content); // 文本消息 if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { logger.info("进入方法内"); QueryWrapper<TypechoContents> queryWrapper = new QueryWrapper<>(); queryWrapper.lambda() .like(TypechoContents::getTitle, content) .or() .like(TypechoContents::getText, content); List<TypechoContents> typechoContentsList = typechoContentsMapper.selectTypechoContentsList(content); logger.info("数据查询完成:" + typechoContentsList.size()); if (typechoContentsList.size() <= 0) { //自动回复 TextMessage text = new TextMessage(); String string = "未找到要查询的内容,请换个关键字试试"; text.setContent(string); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime()); text.setMsgType(msgType); respMessage = MessageUtil.textMessageToXml(text); return respMessage; } else if (typechoContentsList.size() > 1) { TextMessage text = new TextMessage(); StringBuilder stringBuilder = new StringBuilder("搜索到以下内容:\r\n"); for (TypechoContents typechoContents : typechoContentsList) { stringBuilder.append("<a href='https://www.xiangcaowuyu.net/").append(typechoContents.getCategory()).append("/").append(typechoContents.getSlug()).append(".html'>").append(typechoContents.getTitle()).append("</a>"); stringBuilder.append("\r\n"); } text.setContent(stringBuilder.toString()); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime()); text.setMsgType(msgType); respMessage = MessageUtil.textMessageToXml(text); return respMessage; } else { //一条回复图文消息 NewsMessage newsMessage = new NewsMessage(); newsMessage.setArticles(new ArrayList<>()); newsMessage.setArticleCount(typechoContentsList.size()); newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUserName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType("news"); for (TypechoContents typechoContents : typechoContentsList) { Article article = new Article(); article.setUrl("https://www.xiangcaowuyu.net/" + typechoContents.getCategory() + "/" + typechoContents.getSlug() + ".html"); article.setDescription(typechoContents.getTitle()); List<String> imageUrlList = MessageUtil.getMatchString(typechoContents.getText()); if (imageUrlList.size() <= 0) { article.setPicUrl("https://www.xiangcaowuyu.net/usr/themes/xiangcaowuyu/assets/img/logo.png"); } else { logger.info(imageUrlList.get(0)); article.setPicUrl(imageUrlList.get(0)); } article.setTitle(typechoContents.getTitle()); newsMessage.getArticles().add(article); } respMessage = MessageUtil.newsMessageToXml(newsMessage); return respMessage; } } // 事件推送 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { String eventType = requestMap.get("Event");// 事件类型 // 订阅 if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { //文本消息 TextMessage text = new TextMessage(); logger.info(XiangCaoWuYuConfig.getResp()); text.setContent(XiangCaoWuYuConfig.getResp().replace("<br/>","\n")); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setCreateTime(new Date().getTime()); text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); respMessage = MessageUtil.textMessageToXml(text); return respMessage; } // 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息 else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// 取消订阅 } } } catch (Exception e) { logger.error("error......"); } return respMessage; }
2021年05月02日
1,025 阅读
0 评论
0 点赞
2021-04-28
微信公众号开发之公众号基础配置
微信公众号开发的前提是我们要对公众号进行正确的配置。公众号的配置包括两部分:配置白名单,只有配置了白名单我们才能获取到access_token。配置服务器,正确配置服务器后,微信公众号才能将消息转到我们服务端进行处理。配置白名单白名单的配置比较简单。我们打开公众号,左侧找到【安全中心】,点击之后,找到【IP白名单】,点击【查看】按钮,在打开的界面,输入我们的服务器的 IP ,注意,这里输入的是 IP。启用服务器配置进入微信公众号后台,选择【开发】→【基本设置】,打开如下界面下面我们对服务器的几个参数进行将要说明:参数说明服务器地址(URL)我们在启用服务器配置时,微信会通过我们配置的服务器地址,发送一条 GET 请求。请求会携带以下参数序号参数名称描述1signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。2timestamp时间戳3nonce随机数4echostr随机字符串我们在请求中,通过检验 signature 对请求进行校验。若确认此次 GET 请求来自微信服务器,我们只需要原样返回 echostr 参数内容,代表接入成功,否则接入失败。TokenToken 可由我们任意填写,用作生成签名(该 Token 会和接口 URL 中包含的 Token 进行比对,从而验证安全性),主要用于后期调用接口使用。EncodingAESKeyEncodingAESKey 我们可以手动填写或随机生成,将用作消息体加解密密钥。消息加解密方式明文模式、兼容模式和安全模式。模式的选择与服务器配置在提交后都会立即生效,请开发者谨慎填写及选择。加解密方式的默认状态为明文模式,选择兼容模式和安全模式需要提前配置好相关加解密代码。后台代码后台代码,我们主要用于对 URL 的请求进行响应。首先我们需要定义工具类,用于签名验证SignUtil.java/** * 校验签名 * @param signature 签名 * @param timestamp 时间戳 * @param nonce 随机数 * @return true 成功,false 失败 */ public static boolean checkSignature(String signature,String timestamp, String nonce){ String checktext = null; if(null != signature){ //对Token,timestamp nonce 按字典排序 String [] paramArr = new String[] {token, timestamp, nonce}; Arrays.sort(paramArr); //将排序后的结果拼成一个字符串 String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]); try { MessageDigest md = MessageDigest.getInstance("SHA-1"); //对接后的字符串进行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); checktext = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } //将加密后的字符串与signature进行对比 return checktext != null ? checktext.matches(signature.toUpperCase()) :false; } /** * 将字节数组转化为16进制字符串 * @return 字符串 */ private static String byteToStr(byte[] byteArrays) { String str=""; for (int i = 0; i < byteArrays.length; i++){ str += byteToHexStr(byteArrays[i]); } return str; } /** * 将字节转化为十六进制字符串 * @param myByte 字节 * @return 字符串 */ private static String byteToHexStr(byte myByte) { char[] Digit = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[] tempArr = new char[2]; tempArr[0] = Digit[(myByte >>> 4)&0X0F]; tempArr[1] = Digit[myByte & 0x0F]; String str = new String(tempArr); return str; }配置controller响应URL请求我们在微信公众号后台配置的URL就是我们后台对应的URL。并且是 GET 请求。增加WeChatController.java@RestController @RequestMapping("wechat") public class WeChatController { private final Logger logger = LoggerFactory.getLogger(WeChatController.class); /** * 服务器配置启用设置 * @param signature 签名 * @param timestamp 时间戳 * @param nonce 随机数 * @param echostr 随机字符串 * @return 随机字符串 * @throws Exception 异常信息 */ @GetMapping("official") public String authentication(@RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr) throws Exception { if (SignUtil.checkSignature(signature, timestamp, nonce)) { System.out.println(echostr); return echostr; } return""; } }启用服务器配置后台代码配置完成后,我们上传到外网服务器,点击启用按钮,系统提示操作成功,并且界面如下所示,代表我们配置成功。
2021年04月28日
2,019 阅读
1 评论
2 点赞