微信公众号开发(一)前期 配置
微信公众号开发步骤:
一:有一个公众号,可以是测试公众号,也可以是客户提供的已经申请好的公众号
申请测试公众号的网址:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
申请步骤比较简单,这里就不多阐述了。
我开发的主要是客户提供的公众号,一些特殊权限(支付)已经得到开通,可以直接使用,关于测试公众号的某些特殊权限,本人并没有经验。
二:登录到微信公众平台进行开发配置,在登录时可能需要管理员授权,这个就自己联系你们的客户,管理员授权,你才能登录。
登录网址:https://mp.weixin.qq.com/(微信公众平台--官网)
管路员授权之后,我们就可以开始配置了,首先是“基本配置”
首先,来到开发-->基本配置,这里我在初次开发的时候,不知道这里的url应该怎样填写,经过测试,这里的路径主要是一个外界能访问的路径。我的后台开发框架是springMVC,用户访问主要是通过controller,于是我新建了一个项目叫做WXTest,配置好springMVC之后写了一个controller,里面有一个方法叫Test,它的value值是"test",至于如何配置springMVC的项目,这里就不阐述了,如果有需要,我会单独写一篇文章阐述如何搭建一个简单的springNVC的框架。
好了,配置好controller之后,我们可以通过本地访问:http://127.0.0.1:8080/WXTest/test 这样就可以访问到了,但是如何能让微信服务器访问我的controller,如果没有公网,可以通过花生壳的内网映射,如果有公网,可以直接通过公网的ip(开通了80端口的)访问,这里使用的是花生壳,壳域名就是http://uranusyiju.6655.la,通过花生的内网映射,我可以将其映射到我的8080端口,于是http://uranusyiju.6655.la就相当于我的http://127.0.0.1:8080,所以我这里的url配置就填写http://uranusyiju.6655.la/WXTest(项目名)/test(controller的方法路径),配置好url之后,至于token,主要是用于url的验证,你配置了之后,微信访问你的controller的时候会将这个数据发送于你的后台,你根据token判断是否是微信服务器在访问你,然后返回相应的数据,下面会提到,所以这里的token就是一个双方请求的标识而已,不必过于复杂。填写完token之后,下面的EncodingAESKey可以随机生成,然后确认没问题,点击提交,这里就会开始验证了,所以前期需要将你的框架搭建好,确保路径可以正确访问,以及返回正确的交互数据(如果你controller里的方法写得不对,则无法验证通过验证(下面会提到如何写controller的方法),验证通过了,最后,也是最重要的一个地方,请在外面选择“启用”。
***下面是controller的代码:
@Controller
public class WeixinController{
@Resource
UserSubscribeService userSubscribeService;
@Resource
UserUnSubscribeService userUnSubscribeService;
@Resource
UserScanQrCodeService userScanQrCodeService;
// 唯藌的url、token接口设置(来自微信服务器的消息)
@RequestMapping("/test" )
@ResponseBody
public void weixinUrlSet(HttpServletRequest request,
HttpServletResponse response) throws IOException {
System. out.println("进入test,微信的访问会有2种方式post以及get,get只是做url验证。 " );
boolean isGet = request .getMethod().toLowerCase().equals("get");
if (isGet ) {
System. out.println("request:" + request .toString());
//get方法,一般用于微信服务器与本机服务器开发的基本配置(就是token验证,确定服务器是你的)
String str = access( request, response);
response.getWriter().write( str);
} else {
//post方法,一般是用户的事件处理(例如:关注/取消关注、点击按钮、发送消息....)
System. out.println("enter post" );
try {
// 接收消息并返回消息
String str = acceptMessage( request, response);
response.getWriter().write( str);
} catch (IOException e ) {
e.printStackTrace();
}
}
}
/**
* 验证URL真实性
*
* @author morning
* @date 2015年2月17日 上午10:53:07
* @param request
* @param response
* @return String
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
private String access(HttpServletRequest request ,
HttpServletResponse response) {
// 验证URL真实性
System. out.println("进入验证access" );
String signature = request .getParameter("signature"); // 微信加密签名
String timestamp = request .getParameter("timestamp"); // 时间戳
String nonce = request.getParameter( "nonce");// 随机数
String echostr = request.getParameter( "echostr");// 随机字符串
List<String> params = new ArrayList<String>();
//WeixinStaticData. TOKEN就是配置时的token
params.add(WeixinStaticData. TOKEN);
params.add( timestamp);
params.add( nonce);
// 1. 将token、timestamp、nonce三个参数进行字典序排序
Collections. sort(params, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1 .compareTo(o2);
}
});
// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
String str = params.get(0) + params.get(1) + params.get(2);
MessageDigest crypt;
try {
crypt = MessageDigest. getInstance("SHA-1");
crypt.reset();
try {
crypt.update( str.getBytes( "UTF-8"));
String temp = WeixinTool. batySHA1(crypt.digest());
if (temp .equals(signature )) {
System. out.println("成功返回 echostr:" + echostr);
return echostr ;
} else {
System. out.println("失败 认证" );
}
} catch (UnsupportedEncodingException e1 ) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} catch (NoSuchAlgorithmException e1 ) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return null ;
}
private String acceptMessage(HttpServletRequest request,
HttpServletResponse response) throws IOException {
Map<String, String> reqMap = MessageUtil. parseXml(request);
System. out.println("reqMap:" +reqMap .toString());
String fromUserName = reqMap .get("FromUserName" );
String toUserName = reqMap .get("ToUserName" );
String msgType = reqMap.get( "MsgType");
BaseMsg msg = null;// 要发送的消息
// 事件推送
if (msgType .equals(ReqType.EVENT)) {
// 事件类型
String eventType = reqMap .get("Event" );
// 二维码事件
String ticket = reqMap.get( "Ticket");
if (ticket != null) {
String eventKey = reqMap.get( "EventKey");
QrCodeEvent event = new QrCodeEvent(eventKey , ticket);
buildBasicEvent( reqMap, event);
msg = handleQrCodeEvent( event);
}
// 关注
if (eventType .equals(EventType.SUBSCRIBE)) {
//根据reqMap 可以判断用户是否扫带有有参数的二维码
BaseEvent event = new BaseEvent();
buildBasicEvent( reqMap, event);
msg = handleSubscribe( event, reqMap);
}
// 取消关注
else if (eventType .equals(EventType.UNSUBSCRIBE)) {
BaseEvent event = new BaseEvent();
buildBasicEvent( reqMap, event);
msg = handleUnsubscribe( event);
}
// 点击菜单拉取消息时的事件推送
else if (eventType .equals(EventType.CLICK)) {
System. out.println("点击菜单拉取消息时的事件推送" );
String eventKey = reqMap.get( "EventKey");
MenuEvent event = new MenuEvent(eventKey );
buildBasicEvent( reqMap, event);
msg = handleMenuClickEvent( event);
}
// 点击菜单跳转链接时的事件推送
else if (eventType .equals(EventType.VIEW)) {
System. out.println("点击菜单跳转链接时的事件推送" );
String eventKey = reqMap.get( "EventKey");
MenuEvent event = new MenuEvent(eventKey );
System. out.println("event:" +event .toXml().toString());
buildBasicEvent( reqMap, event);
msg = handleMenuViewEvent( event);
}
// 上报地理位置事件
else if (eventType .equals(EventType.LOCATION)) {
double latitude = Double.parseDouble(reqMap.get("Latitude"));
double longitude = Double.parseDouble(reqMap.get("Longitude"));
double precision = Double.parseDouble(reqMap.get("Precision"));
LocationEvent event = new LocationEvent(latitude , longitude,
precision);
buildBasicEvent( reqMap, event);
msg = handleLocationEvent( event);
}
} else {// 接受普通消息
// 文本消息
if (msgType .equals(ReqType.TEXT)) {
String content = reqMap.get( "Content");
TextReqMsg textReqMsg = new TextReqMsg(content);
buildBasicReqMsg( reqMap, textReqMsg);
msg = handleTextMsg( textReqMsg);
}
// 图片消息
else if (msgType .equals(ReqType.IMAGE)) {
String picUrl = reqMap.get( "PicUrl");
String mediaId = reqMap.get( "MediaId");
ImageReqMsg imageReqMsg = new ImageReqMsg(picUrl, mediaId);
buildBasicReqMsg( reqMap, imageReqMsg);
msg = handleImageMsg( imageReqMsg);
}
// 音频消息
else if (msgType .equals(ReqType.VOICE)) {
String format = reqMap.get( "Format");
String mediaId = reqMap.get( "MediaId");
String recognition = reqMap .get("Recognition" );
VoiceReqMsg voiceReqMsg = new VoiceReqMsg(mediaId, format,
recognition);
buildBasicReqMsg( reqMap, voiceReqMsg);
msg = handleVoiceMsg( voiceReqMsg);
}
// 视频消息
else if (msgType .equals(ReqType.VIDEO)) {
String thumbMediaId = reqMap .get("ThumbMediaId" );
String mediaId = reqMap.get( "MediaId");
VideoReqMsg videoReqMsg = new VideoReqMsg(mediaId, thumbMediaId);
buildBasicReqMsg( reqMap, videoReqMsg);
msg = handleVideoMsg( videoReqMsg);
}
// 地理位置消息
else if (msgType .equals(ReqType.LOCATION)) {
double locationX = Double.parseDouble(reqMap.get("Location_X"));
double locationY = Double.parseDouble(reqMap.get("Location_Y"));
int scale = Integer.parseInt (reqMap .get("Scale" ));
String label = reqMap.get( "Label");
LocationReqMsg locationReqMsg = new LocationReqMsg(locationX,
locationY, scale , label );
buildBasicReqMsg( reqMap, locationReqMsg);
msg = handleLocationMsg( locationReqMsg);
}
// 链接消息
else if (msgType .equals(ReqType.LINK)) {
String title = reqMap.get( "Title");
String description = reqMap .get("Description" );
String url = reqMap.get( "Url");
LinkReqMsg linkReqMsg = new LinkReqMsg(title, description, url );
buildBasicReqMsg( reqMap, linkReqMsg);
msg = handleLinkMsg( linkReqMsg);
}
}
if (msg == null) {
// 回复空串是微信的规定,代表不回复
return "" ;
}
msg.setFromUserName( toUserName);
msg.setToUserName( fromUserName);
return msg .toXml();
}
/*******************************************下面为处理每个请求的具体逻辑入口*******************************/
/**
* 处理文本消息
*/
protected BaseMsg handleTextMsg(TextReqMsg msg ) {
return handleDefaultMsg(msg );
}
/**
* 处理图片消息
*/
protected BaseMsg handleImageMsg(ImageReqMsg msg ) {
return handleDefaultMsg(msg );
}
/**
* 处理语音消息
*/
protected BaseMsg handleVoiceMsg(VoiceReqMsg msg ) {
return handleDefaultMsg(msg );
}
/**
* 处理视频消息
*/
protected BaseMsg handleVideoMsg(VideoReqMsg msg ) {
return handleDefaultMsg(msg );
}
/**
* 处理地理位置消息
*/
protected BaseMsg handleLocationMsg(LocationReqMsg msg) {
return handleDefaultMsg(msg );
}
/**
* 处理链接消息
*/
protected BaseMsg handleLinkMsg(LinkReqMsg msg ) {
return handleDefaultMsg(msg );
}
/**
* 处理扫描带参数二维码事件
*/
protected BaseMsg handleQrCodeEvent(QrCodeEvent event ) {
System. out.println("处理扫描带参数二维码事件" );
System. out.println("event:" +event .toXml());
userScanQrCodeService.handleScanQrCode();
return handleDefaultEvent(event );
}
/**
* 处理上报地理位置事件
*/
protected BaseMsg handleLocationEvent(LocationEvent event) {
return handleDefaultEvent(event );
}
/**
* 处理点击菜单拉取消息时的事件推送
*/
protected BaseMsg handleMenuClickEvent(MenuEvent event) {
return handleDefaultEvent(event );
}
/**
* 处理点击菜单跳转链接时的事件推送
*/
protected BaseMsg handleMenuViewEvent(MenuEvent event ) {
return handleDefaultEvent(event );
}
/**
* 处理关注事件
* 默认不回复
*/
protected BaseMsg handleSubscribe(BaseEvent event , Map<String,String> reMap) {
System. out.println("用户关注======" );
userSubscribeService.handleSubscribeService(event ,reMap );
String msg = userSubscribeService.displayAutoReturn();
return new TextMsg(msg);
}
/**
* 处理取消订阅事件 <br>
* 默认不回复
*/
protected BaseMsg handleUnsubscribe(BaseEvent event ) {
System. out.println("用户取消关注======" );
userUnSubscribeService.handleUnSubscribe(event );
return null ;
}
/**
* 处理消息的默认方式 <br>
* 如果不重写该方法,则默认不返回任何消息
*/
protected BaseMsg handleDefaultMsg(BaseReqMsg msg ) {
return null ;
}
/**
* 设置处理事件的默认方式 <br>
* 如果不重写该方法,则默认不返回任何消息
*/
protected BaseMsg handleDefaultEvent(BaseEvent event ) {
return null ;
}
/**
* 为事件普通消息对象添加基本参数 <br>
* 参数包括:MsgId、MsgType、FromUserName、ToUserName和CreateTime
*/
private void buildBasicReqMsg(Map<String, String> reqMap, BaseReqMsg reqMsg) {
addBasicReqParams( reqMap, reqMsg);
reqMsg.setMsgId( reqMap.get( "MsgId"));
}
/**
* 为事件推送对象添加基本参数 <br>
* 参数包括:Event、MsgType、FromUserName、ToUserName和CreateTime
*/
private void buildBasicEvent(Map<String, String> reqMap, BaseEvent event) {
addBasicReqParams( reqMap, event);
event.setEvent( reqMap.get( "Event"));
}
/**
* 为请求对象添加基本参数,包括MsgType、FromUserName、ToUserName和CreateTime <br>
* 请求对象包括普通消息和事件推送
*/
private void addBasicReqParams(Map<String, String> reqMap, BaseReq req) {
req.setMsgType( reqMap.get( "MsgType"));
req.setFromUserName( reqMap.get( "FromUserName"));
req.setToUserName( reqMap.get( "ToUserName"));
req.setCreateTime(Long. parseLong(reqMap.get("CreateTime")));
}
}
上面的代码很多,其实浏览一下能知道大概意思就可以了。
ok,现在配置以及初步处理微信请求都已经准备好了,还有很多其他配置,在需要的时候会提到,下面就开始进入正式的开发了.....