微信公众号开发笔记(二):微信分享接口实现和问题

上篇文章写到了公众号开发的基本配置(不明白的可以参考https://blog.csdn.net/TOP__ONE/article/details/78183209),这里继续写微信分享接口功能代码实现。

如果以下代码涉及到微信工具类方法,而我没有提到的,请到链接下载:https://download.csdn.net/download/top__one/10875681

实现微信页面的分享自定义接口功能,需要先配置js-sdk以下数据项,所以需要先获取这些数据项。

wx.config({
	debug : false,
	appId : appId,
	timestamp : timestamp,
	nonceStr : nonceStr,
	signature : signature,
	jsApiList : [ 'chooseWXPay', 'checkJsApi', 'closeWindow',
				'onMenuShareTimeline', 'onMenuShareAppMessage' ]
	});

1、后台接口程序开发

先对跳转分享页面action的基本编写,进行必要数据的封装

    /**
	 * 分享页初始化
	 * 
	 * @param map
	 * @return
	 */
	@RequestMapping(value = "/toshare")
	public String index(Model model, @RequestParam Map<String, String> map) {
		logger.info("进入分享主页");
		// 为了获取调取微信的jsdk的方法,进行配置数据项的封装
		Map<String, String> sign = getSingture(request);
                 model.addAttribute("sign", sign);
        //此host为访问域名,即项目域名,要和js安全域名一致
		String host = PropertieSingle.getInstance().getProperty("host");
		model.addAttribute("host", host);
		return "shareIndex";
	}

     /**
	 * 获取singture的公共方法
	 * 
	 * @param request
	 * @return
	 */
	private static Map<String, String> getSingture(HttpServletRequest request) {
		// response.setContentType("text/javascript;charset=utf-8");
		// FiXME 通过接口获取access_token,再通过access_token获取jsapi_ticket
		String wechatTicket = WechatServiceUtil.getWechatTicket();
		String strBackUrl = "http://" + request.getServerName() // 服务器地址
//				 + ":"
//				 + request.getServerPort() //端口号
				+ request.getContextPath() // 项目名称
				+ request.getServletPath(); // 请求页面或其他地址
		if (!StringUtils.isEmpty(request.getQueryString()))
			strBackUrl += "?" + (request.getQueryString()); // 参数
		Map<String, String> sign = Sign.sign(wechatTicket, strBackUrl);
		sign.put("appId", PropertieSingle.getInstance().getProperty("APPID"));
		return sign;
	}

其中里面的Sign工具类代码

package com.chinatsp.wechat.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.UUID;


public class Sign {
    public static void main(String[] args) {
        String jsapi_ticket = "jsapi_ticket";

        // 注意 URL 一定要动态获取,不能 hardcode
        String url = "http://example.com";
        Map<String, String> ret = sign(jsapi_ticket, url);
        for (Map.Entry entry : ret.entrySet()) {
            System.out.println(entry.getKey() + ", " + entry.getValue());
        }
    };

    /**
     * 这是jssdk中需要的那个singture签名
     * @param jsapi_ticket
     * @param url
     * @return
     */
    public static Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();//定义的字符串
        String timestamp = create_timestamp();//同上
        String string1;
        String signature = "";
        
        System.out.println(nonce_str);
        System.out.println(timestamp);
        System.out.println(jsapi_ticket);
        System.out.println(url);
        
        //注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket +
                  "&noncestr=" + nonce_str +
                  "&timestamp=" + timestamp +
                  "&url=" + url;
        System.out.println(string1);

        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");//加密方法
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }
	
       
	//加密方法
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    public static String create_nonce_str() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }
}

其中wechatTicket的获取代码如下,另附TicketAPI.

/**
*这里是用到了redis缓存,tiket的有效期为7200s,我设置redis过期时间为7000s
*tiket每天获取次数有限,所以尽量做一个缓存处理
*/
public static String  getWechatTicket(){
        //获取TICKET,从redis中
		String ticket= RedisUtils.getCachRedisData(WechatBean.TICKET);
		if(ticket==null){
            //获取token
			Ticket t= TicketAPI.ticketGetticket(getWechatToken());
	RedisUtils.setCachRedisDataTimeLong(WechatBean.TICKET,Integer.parseInt(WechatBean.WECHAT_TOKEN_TIME+""),t.getTicket());
			return t.getTicket();
		}else{
			return ticket;
		}
	}

 另附TicketAPI.ticketGetticket方法代码

package weixin.popular.api;

import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;

import weixin.popular.bean.Ticket;
import weixin.popular.client.LocalHttpClient;

/**
 * JSAPI ticket
 * @author LiYi
 *
 */
public class TicketAPI extends BaseAPI{

	/**
	 * 获取 jsapi_ticket
	 * @param access_token
	 * @return
	 */
	public static Ticket ticketGetticket(String access_token){
		HttpUriRequest httpUriRequest = RequestBuilder.post()
				.setUri(BASE_URI + "/cgi-bin/ticket/getticket")
				.addParameter("access_token",access_token)
				.addParameter("type", "jsapi")
				.build();
		return LocalHttpClient.executeJsonResult(httpUriRequest,Ticket.class);
	}
}

至此,后台获取token、ticket、sign等参数,然后跳转前台页面,进行页面数据配置

2、进行页面js-sdk数据配置,通过config接口注入权限验证配置

首先需要引用js: <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

注:请注意,原有的 wx.onMenuShareTimeline、wx.onMenuShareAppMessage、wx.onMenuShareQQ、wx.onMenuShareQZone 接口,即将废弃。请尽快迁移使用客户端6.7.2及JSSDK 1.4.0以上版本支持的 wx.updateAppMessageShareData、updateTimelineShareData 接口。

我此处引用的是1.2.0版本js,此博文也是在1.2.0版本基础上进行的。后面后附上我试用1.4版本js的问题。

在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js

如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。

备注:支持使用 AMD/CMD 标准模块加载方法加载

页面代码:

<script type="text/javascript">
	$(document).ready(
			function() {
				var appId = $("#appId").val();
				var timestamp = $("#timestamp").val();
				var nonceStr = $("#nonceStr").val();
				var signature = $("#signature").val();
				wx.config({
					debug : true,
					appId : appId,
					timestamp : timestamp,
					nonceStr : nonceStr,
					signature : signature,
					jsApiList : [ 'chooseWXPay', 'checkJsApi', 'closeWindow',
							'onMenuShareTimeline', 'onMenuShareAppMessage' ]
				});

				wx.error(function(res) {
					//alert(JSON.stringify(res));
					// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
				});
				wx.checkJsApi({
					jsApiList : [ 'checkJsApi', 'onMenuShareTimeline',
							'onMenuShareAppMessage' ], // 需要检测的JS接口列表,所有JS接口列表见附录2,
					success : function(res) {
						//alert(res);
						// 以键值对的形式返回,可用的api值true,不可用为false
						// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
					}
				});

			});

	wx.ready(function() {
			var title = "分享朋友圈?";
			var desc = "晒能力,抢大红包";
			var link = "https://blog.csdn.net/TOP__ONE/article/details/78183209";
			var imgUrl ="https://blog.csdn.net/TOP__ONE/share_logo.png";
				wx.onMenuShareTimeline({
							title : title, // 分享标题
							link : link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
							imgUrl : imgUrl, // 分享图标
							success : function() {
								// 用户确认分享后执行的回调函数
								$.ajax({
								   type: "POST",
								   async: false,
								   url: "",
								   success : function(msg) {
										
									}
								}); 
							},
							cancel : function() {
								// 用户取消分享后执行的回调函数
								//window.location.href='*.action';
							}
						});

				wx.onMenuShareAppMessage({
					title : title, // 分享标题
					desc : desc, // 分享描述
					link : link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
					imgUrl : imgUrl, // 分享图标
					type : '', // 分享类型,music、video或link,不填默认为link
					dataUrl : '', // 如果type是music或video,则要提供数据链接,默认为空
					success : function() {
						// 用户确认分享后执行的回调函数
						$.ajax({
						   type: "POST",
						   async: false,
						   url: "",
						   success : function(msg) {
								
							}
						}); 
					},
					cancel : function() {
						// 用户取消分享后执行的回调函数
					}
				});

			});

</script>

此代码中appId、timestamp、nonceStr、signature均为后台封装的sign中的值,通过自己方法获取即可。

wx.config中的debug更改为true可查看配置情况。

至此,微信分享接口demo即开发完毕。更详细官方链接https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

--------------------------------------贞操 分割线---------------------------------------

在微信分享过程中也遇到了一些问题,比如

1、签名错误invalid signature,需要检查页面wx.config中的字段是否正确,需要检查js安全域名是否与项目域名一致。一般检查顺序如下:

(1)确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。

(2)确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。

(3)确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。

(4)确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。

(5)确保一定缓存access_token和jsapi_ticket。

(6)确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

如果后台返回的config 信息 与微信签名工具校验一致的话,那很有可能是前端访问的url和后台生成签名的url不一致导致,看是否需要encodeURIComponent或者decode下。

2、安卓分享成功,ios分享不成功问题

(1) IOS分享的时候图片的链接地址 和 link 都需要在 JS安全域名下并且带http头。

(2)网上搜的资源还有说是 图片大小不能超过32kb,这个不知道有没有用,没试过

(3)分享的link等url中包含中文会导致ios分享不成功,需要对中文字段进行编码处理 例:

  var link = "*.action?name=" + encodeURIComponent($('#uname').val());

因为安卓可以解析,IOS可能解析不了

(4)微信版本低,也有可能导致分享不成功

更多错误代码请详见https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115附录5

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值