zoukankan      html  css  js  c++  java
  • 微信公众号开发第一版

    1. 微信公众号是一个非常轻便的项目平台,我主要是做web应用,在我看来,公众号就是一个web项目存放平台,通过view类型的菜单,可以存放多个web应用。
    2. 公众号开发入门:

    配置服务器地址————即为你的公众号token验证的controller(我用的SpringMVC),这个controller比较简单,但是极为重要,这是整个项目的入口,关于微信公众号的最基本操作都放在里面,比如token验证,消息回复,其中也有一定的代码规范,比如异常处理,又比如在五秒内必须做出对微信消息的响应,如果代码不规范就会造成你的控制太明明打印的是验证成功,微信公众平台却显示配置失败的情况。

    令牌(Token)————这个和你检验程序中的token一致就可以。
    消息加解秘钥————我选择随机生成,(43位太麻烦,建议大家还是随机生成吧)。
    消息加解密方式————如果要追求安全系数可以选择安全模式,但要自己写加密程序。
    wechatcontroller:
    @Autowired
    private WechatPostService wechatPostService;

    @RequestMapping(value = "/wechat/connect", method = { RequestMethod.GET, RequestMethod.POST })
    @ResponseBody
    public void ConnectWeixin(HttpServletRequest request, HttpServletResponse response) throws IOException, DocumentException {
    	// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
    	request.setCharacterEncoding("UTF-8"); // 微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;
    	response.setCharacterEncoding("UTF-8"); // 在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;
    	boolean isGet = request.getMethod().toLowerCase().equals("get");
    
    	PrintWriter out = response.getWriter();
    	try {
    		if (isGet) {
    			String signature = request.getParameter("signature");
    			String timestamp = request.getParameter("timestamp");
    			String nonce = request.getParameter("nonce");
    			String echostr = request.getParameter("echostr");
    			if (CheckUtils.checkSignature(signature, timestamp, nonce)) {
    				System.out.println("验证成功!");
    				out.write(echostr);
    			} else {
    				System.out.println("验证失败!");
    			}
    		} else {
    			String respMessage = "异常消息!";
    			respMessage = wechatPostService.weixinPost(request);
    			out.write(respMessage);
    			System.out.println(respMessage);
    		}
    	} catch (Exception e) {
    		System.out.println("链接失败!");
    	}finally {
    		out.close();
    	}
    }
    

    chickUtil:
    /**
    * 开发者模式-开发者自己填写的 token (令牌)
    */
    private static final String token = "lm03";

    /**
     * 功能:验证消息的确来自微信服务器
     */
    public static boolean checkSignature(String signature,String timestamp,String nonce) {
    	String[] arr = new String[] {token,timestamp,nonce};
    	//排序
    	Arrays.sort(arr);
    	//生成,拼接字符串
    	StringBuffer content = new StringBuffer();
    	for(int i=0; i<arr.length; i++) {
    		content.append(arr[i]);
    	}
    	//sha1加密
    	String shaString = getSha1(content.toString());
    	return shaString.equals(signature);
    }
    	/**
    	* sha1加密
    	* @param str
    	* @return
    	*/
    	public static String getSha1(String str){
    	if (null == str || 0 == str.length()){
    		return null;
    	}
    	char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    	'a', 'b', 'c', 'd', 'e', 'f'};
    	try {
        	MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
        	mdTemp.update(str.getBytes("UTF-8"));
        	byte[] md = mdTemp.digest();
        	int j = md.length;
        	char[] buf = new char[j * 2];
        	int k = 0;
        	for (int i = 0; i < j; i++) {
        	byte byte0 = md[i];
        	buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
        	buf[k++] = hexDigits[byte0 & 0xf];
    	}
        	return new String(buf);
    	} catch (Exception e) {
    		return null;
    	}
    }
    

    WechatPostService:
    @SuppressWarnings("null")
    public String weixinPost(HttpServletRequest request) {

    	String reqMessage = null;
    
    	try {
    		Map<String, String> map = MessageUtils.xmlToMap(request);
    		String fromUserName = map.get("FromUserName");
    		String toUserName = map.get("ToUserName");
    		String msgType = map.get("MsgType");
    		String content = map.get("Content");
    		if (MessageUtils.MESSAGE_TEXT.equals(msgType)) {
    			if ("1".equals(content)) {
    				System.out.println(fromUserName);
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, MessageUtils.firstMenu());
    			} else if ("2".equals(content)) {
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, MessageUtils.secondMenu());
    			} else {
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, MessageUtils.menuText());
    			}
    		} else {
    			String eventType = map.get("Event");
    			if (MessageUtils.MESSAGE_SUBSCRIBE.equals(eventType)) {
    				// 关注时创建菜单
    				AccessToken accessToken = TokenUtils.getAccessToken();
    				/*System.out.println("票据:" + accessToken.getToken());
    				System.out.println("时效:" + accessToken.getExpiresIn());*/
    
    				String menu = JSONObject.fromObject(TokenUtils.InitMenu()).toString();
    				System.out.println(menu);
    				int result = TokenUtils.CreateMenu(accessToken.getToken(), menu);
    				if (result == 0) {
    					System.out.println("创建菜单成功");
    				} else {
    					System.out.println("错误码:" + result);
    				}
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, MessageUtils.menuText());
    			} 
    			
    			else if (MessageUtils.MESSAGE_CLICK.equals(eventType)) {
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, MessageUtils.menuText());
    			} else if (MessageUtils.MESSAGE_VIEW.equals(eventType)) {
    				String url = map.get("EventKey");
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, url);
    			} else if (MessageUtils.MESSAGE_UNSUBSCRIBE.equals(eventType)) {
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, "取消关注");
    			} else {
    				String key = map.get("EventKey");
    				reqMessage = MessageUtils.initText(toUserName, fromUserName, key);
    			}
    		}
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    
    	return reqMessage;
    }
    
    1. 消息回复:
      微信公众号的消息都是以XML的格式发送的,不管是客户发送给微信公众号还是微信公众号发给客户,都是如此,除文本消息外,其他的都要获取相应的数据进行封装。
      MessageUtils:
      public static final String MESSAGE_TEXT = "text";
      public static final String MESSAGE_IMAGE = "image";
      public static final String MESSAGE_VOICE = "voice";
      public static final String MESSAGE_VIDEO = "video";
      public static final String MESSAGE_LINK = "link";
      public static final String MESSAGE_LOCATION = "location";
      public static final String MESSAGE_EVENT = "event";
      public static final String MESSAGE_SUBSCRIBE = "subscribe";
      public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";
      public static final String MESSAGE_CLICK = "CLICK";
      public static final String MESSAGE_VIEW = "VIEW";
      /*

      • 封装响应
        */
        public static String initText(String toUserName,String fromUserName,String content){
        TextMessage text = new TextMessage();
        text.setFromUserName(toUserName);
        text.setToUserName(fromUserName);
        text.setMsgType(MessageUtils.MESSAGE_TEXT);
        text.setCreateTime(new Date().getTime());
        text.setContent(content);
        return textMessageToxml(text);
        }

      /**

      • 主菜单
        /
        public static String menuText(){
        StringBuffer sb = new StringBuffer();
        sb.append("欢迎您的关注,请按照菜单提示进行操作: ");
        sb.append("1、家学通介绍 ");
        sb.append("2、学校风采 ");
        sb.append("回复任意调出此菜单。");
        return sb.toString();
        }
        /
      • 关键字1
        /
        public static String firstMenu(){
        StringBuffer sb = new StringBuffer();
        sb.append("此公众号主要用于家长能够及时了解学生在校学习情况");
        return sb.toString();
        }
        /
      • 关键字2
        /
        public static String secondMenu(){
        StringBuffer sb = new StringBuffer();
        sb.append("学校在近期的活动,如运动会,演讲,竞赛等");
        return sb.toString();
        }
        /
      • 消息请求为XML格式,将XML转为map集合
        */
        public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException{
        Map<String, String> map = new HashMap<String ,String>();
        SAXReader Reader = new SAXReader();
        InputStream inputStream = request.getInputStream();
        Document document = Reader.read(inputStream);
        Element root = document.getRootElement();
        List list = root.elements();
        for (Element element : list) {
        map.put(element.getName(), element.getText());
        }
        inputStream.close();
        return map;

      }
      /*

      • 响应为XML格式,将响应转为XML格式
        */
        public static String textMessageToxml(TextMessage textmessage) {
        XStream stream = new XStream();
        stream.alias("xml", textmessage.getClass());
        return stream.toXML(textmessage);
        }
    2. 菜单创建:
      我这里就不说菜单有什么类型了,对于一个web应用来说,一个view类型的菜单就是一个web项目的入口。在入口中又很多事情可以做,比如网页开发权限的获取,用户信息的获取。还有就是菜单创建的位置,当时我学的时候为这个东西苦恼了很久,最后觉得在用户关注时进行菜单创建比较好,当然这里只是提供我的建议而已,还有很多其他途径。
      代码中替换的都是微信提供的接口
      /**

      • 发起GET请求
        */
        public static JSONObject doGetStr(String url) {
        HttpClientBuilder builder = HttpClientBuilder.create();
        HttpGet httpGet = new HttpGet(url);
        JSONObject object = null;

        try {
        HttpResponse response = builder.build().execute(httpGet);
        HttpEntity entity = response.getEntity();
        if (null != entity) {
        String result = EntityUtils.toString(entity, CHARSET_FORMAT);
        object = JSONObject.fromObject(result);
        }
        } catch (ClientProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        }
        return object;
        }

      /*

      • 发起post请求
        */
        public static JSONObject doPostStr(String url, String outstr) {
        HttpClientBuilder builder = HttpClientBuilder.create();
        HttpPost httpPost = new HttpPost(url);
        JSONObject object = null;
        httpPost.setEntity(new StringEntity(outstr, CHARSET_FORMAT));
        try {
        HttpResponse response = builder.build().execute(httpPost);
        HttpEntity entity = response.getEntity();
        if (null != entity) {
        String result = EntityUtils.toString(entity, CHARSET_FORMAT);
        object = JSONObject.fromObject(result);
        }
        } catch (IOException e) {

         e.printStackTrace();
        

        }
        return object;
        }

      /*

      • 获取 access_token
        */
        public static AccessToken getAccessToken() {
        AccessToken token = new AccessToken();
        String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
        JSONObject json = doGetStr(url);

        if (null != json && json.containsKey("access_token")) {
        token.setToken(json.getString("access_token"));
        token.setExpiresIn(json.getInt("expires_in"));
        }
        return token;
        }

      /*

      • 初始化菜单
        */
        public static Menu InitMenu() throws UnsupportedEncodingException {
        Menu menu = new Menu();

        ViewButton button1 = new ViewButton();
        button1.setName("家学通");
        button1.setType("view");
        button1.setUrl(getCodeUrl());

        ClickButton button2 = new ClickButton();
        button2.setName("更多");
        button2.setType("click");
        button2.setKey("11");

        ClickButton button31 = new ClickButton();
        button31.setName("扫码事件");
        button31.setType("scancode_push");
        button31.setKey("31");

        ClickButton button32 = new ClickButton();
        button32.setName("地理位置");
        button32.setType("location_select");
        button32.setKey("32");

        Button button3 = new Button();
        button3.setName("菜单");
        button3.setSub_button(new Button[] { button31, button32 });

        menu.setButton(new Button[] { button1, button2, button3 });
        return menu;
        }

      /*

      • 创建菜单
        */
        public static int CreateMenu(String token, String menu) {
        int result = 0;
        String url = CREATE_MENU_URL.replace("ACCESS_TOKEN", token);
        JSONObject jsonObject = TokenUtils.doPostStr(url, menu);
        if (jsonObject != null) {
        result = jsonObject.getInt("errcode");
        }
        return result;
        }
    3. 网页开发:
      开始我不得不说一句,微信的网页开发真的很坑,我学的时候真是痛不欲生。
      首先就是微信网页开发的配置,配置的是域名,前面不带http或者HTTPS
      其次就是微信客户信息的获取,首先获取code,由code获取acces_token,再由access_token获取用户信息,这里面有很多坑,第一个:code必须由请求重定向获取,至于为什么请求转发不行,我也不知道,你可以问下马化腾;第二:code的时效性,五分钟内你获取的只能用一次,有木有很坑;第三,这是最坑的一点,明明你只发送了一次请求,但是他却会发送多次,这就造成了我说的第二个问题,重复使用code造成获取不到用户信息,其实第一次是获取到的,但是被后面的覆盖了,最好的办法是只获取一次就存入session,后面想用的时候全部在session中获得,而且,如果你没有设置session时效,除非你退出web应用,否则session一直有效,至于微信发送多次请求的问题,我觉得除了我用映射工具的原因,一定还有其他因素,希望知道的同学可以告诉我。

    4. 最后,有关其他功能比如群发消息,支付接口,等等,虽然我没做过,但是我觉得无非是调用接口,另外,希望大家多看微信的开发文档,虽然微信的开发文档真的很烂。

  • 相关阅读:
    torchline:让Pytorch使用的更加顺滑
    论文笔记系列-AutoFPN
    Latex: 添加IEEE会议论文作者信息
    Latex citation using natbib and footnotesize
    解决 Boost安装:fatal error: bzlib.h: No such file or directory 问题
    将 Graphviz .dot 文件转换为其他格式的图像
    Mac环境下扩容 .vmdk 镜像容量
    解决 dpkg: warning: files list file for package 'x' missing 问题
    Latex 左右引号
    Latex 三线表及设置列数
  • 原文地址:https://www.cnblogs.com/lm-book/p/9150340.html
Copyright © 2011-2022 走看看