一、本节要点
1.消息的加解密
微信加解密包 下载地址:http://qydev.weixin.qq.com/java.zip ,此包中封装好了AES加解密方法,直接调用方法即可。
其中,解密方法为:
//2.获取消息明文:对加密的请求消息进行解密获得明文
WXBizMsgCrypt wxcpt=new WXBizMsgCrypt(WeiXinParamesUtil.token,WeiXinParamesUtil.encodingAESKey,WeiXinParamesUtil.corpId);
result = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postData);
加密方法为:
//6.加密
WXBizMsgCrypt wxcpt=new WXBizMsgCrypt(WeiXinParamesUtil.token,WeiXinParamesUtil.encodingAESKey,WeiXinParamesUtil.corpId);
respMessage = wxcpt.EncryptMsg(respMessage, timestamp, msgType);
2.被动回复消息的流程
用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接收到消息后,再对消息做出相应的回复消息。
接收消息:需先从request请求对象的输入流中获取请求参数和已加密的请求消息,再对已加密的请求消息进行解密操作,即可获得明文。
然后就行对明文消息的业务处理了。
回复消息:封装好回复消息后,需先对回复消息进行加密,获得已已加密消息,然后再通过http请求调用被动回复消息的接口,来发送消息。
二、接收消息服务器配置
接受消息服务器配置好后,用户发送消息时,微信服务器会将消息转发到配置的接受消息服务器url上,即以POST方式转发到 CoreServlet 上。
三、接收消息实体类的封装(req)
参见官方文档的说明
3.1 消息基类——BaseMessage
package com.ray.pojo.message.req; /**
* 消息基类(普通用户 -> 企业微信)
* @author shirayner
*
*/
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;
//企业应用的id,整型。可在应用的设置页面查看
private int AgentID; public String getToUserName() {
return ToUserName;
} public void setToUserName(String toUserName) {
ToUserName = toUserName;
} public String getFromUserName() {
return FromUserName;
} public void setFromUserName(String fromUserName) {
FromUserName = fromUserName;
} public long getCreateTime() {
return CreateTime;
} public void setCreateTime(long createTime) {
CreateTime = createTime;
} public String getMsgType() {
return MsgType;
} public void setMsgType(String msgType) {
MsgType = msgType;
} public long getMsgId() {
return MsgId;
} public void setMsgId(long msgId) {
MsgId = msgId;
} public int getAgentID() {
return AgentID;
} public void setAgentID(int agentID) {
AgentID = agentID;
} }
3.2 文本消息——TextMessage
package com.ray.pojo.message.req; /**
* 文本消息
* @author shirayner
*
*/
public class TextMessage extends BaseMessage {
// 消息内容
private String Content; public String getContent() {
return Content;
} public void setContent(String content) {
Content = content;
}
}
3.3图片消息——ImageMessage
package com.ray.pojo.message.req; /**
* 图片消息
* @author shirayner
*
*/
public class ImageMessage extends BaseMessage {
// 图片链接
private String PicUrl;
// 图片媒体文件id,可以调用获取媒体文件接口拉取
private String MediaId; public String getPicUrl() {
return PicUrl;
} public void setPicUrl(String picUrl) {
PicUrl = picUrl;
} public String getMediaId() {
return MediaId;
} public void setMediaId(String mediaId) {
MediaId = mediaId;
} }
3.4 语音消息——VoiceMessage
package com.ray.pojo.message.req; /**
* 语音消息
* @author shirayner
*
*/
public class VoiceMessage extends BaseMessage {
// 语音媒体文件id,可以调用获取媒体文件接口拉取数据
private String MediaId;
// 语音格式,如amr,speex等
private String Format; public String getMediaId() {
return MediaId;
} public void setMediaId(String mediaId) {
MediaId = mediaId;
} public String getFormat() {
return Format;
} public void setFormat(String format) {
Format = format;
}
}
3.5 视频消息——Video、VideoMessage
Video.java
package com.ray.pojo.message.resp; /**
* @desc : 视频
*
* @author: shirayner
* @date : 2017-8-17 下午2:00:22
*/
public class Video { //视频文件id,可以调用获取媒体文件接口拉取
private String MediaId;
//视频消息的标题
private String Title;
//视频消息的描述
private String Description;
public String getMediaId() {
return MediaId;
}
public void setMediaId(String mediaId) {
MediaId = mediaId;
}
public String getTitle() {
return Title;
}
public void setTitle(String title) {
Title = title;
}
public String getDescription() {
return Description;
}
public void setDescription(String description) {
Description = description;
} }
VideoMessage.java
package com.ray.pojo.message.resp; /**
* @desc : 视频消息
*
* @author: shirayner
* @date : 2017-8-21 上午10:36:33
*/
public class VideoMessage extends BaseMessage {
// 视频
private Video Video; public Video getVideo() {
return Video;
} public void setVideo(Video video) {
Video = video;
}
}
3.6 位置消息——LocationMessage
package com.ray.pojo.message.req; /**
* 位置消息
* @author shirayner
*
*/
public class LocationMessage extends BaseMessage {
// 地理位置维度
private String Location_X;
// 地理位置经度
private String Location_Y;
// 地图缩放大小
private String Scale;
// 地理位置信息
private String Label; public String getLocation_X() {
return Location_X;
} public void setLocation_X(String location_X) {
Location_X = location_X;
} public String getLocation_Y() {
return Location_Y;
} public void setLocation_Y(String location_Y) {
Location_Y = location_Y;
} public String getScale() {
return Scale;
} public void setScale(String scale) {
Scale = scale;
} public String getLabel() {
return Label;
} public void setLabel(String label) {
Label = label;
}
}
3.7 链接消息——LinkMessage
package com.ray.pojo.message.req; /**
* 链接消息
* @author shirayner
*
*/
public class LinkMessage extends BaseMessage {
// 消息标题
private String Title;
// 消息描述
private String Description;
// 封面缩略图的url
private String PicUrl; public String getTitle() {
return Title;
} public void setTitle(String title) {
Title = title;
} public String getDescription() {
return Description;
} public void setDescription(String description) {
Description = description;
} public String getPicUrl() {
return PicUrl;
} public void setPicUrl(String picUrl) {
PicUrl = picUrl;
} }
四、被动回复消息的封装(resp)
4.1 消息基类——BaseMessage
package com.ray.pojo.message.resp; /**
* 消息基类(企业微信 -> 普通用户)
* @author shirayner
*
*/
public class BaseMessage {
// 成员UserID
private String ToUserName;
// 企业微信CorpID
private String FromUserName;
// 消息创建时间 (整型)
private long CreateTime;
// 消息类型
private String MsgType; public String getToUserName() {
return ToUserName;
} public void setToUserName(String toUserName) {
ToUserName = toUserName;
} public String getFromUserName() {
return FromUserName;
} public void setFromUserName(String fromUserName) {
FromUserName = fromUserName;
} public long getCreateTime() {
return CreateTime;
} public void setCreateTime(long createTime) {
CreateTime = createTime;
} public String getMsgType() {
return MsgType;
} public void setMsgType(String msgType) {
MsgType = msgType;
}
}
4.2 文本消息——TextMessage
package com.ray.pojo.message.resp; /**
* 文本消息
* @author shirayner
*
*/
public class TextMessage extends BaseMessage {
// 回复的消息内容
private String Content; public String getContent() {
return Content;
} public void setContent(String content) {
Content = content;
}
}
4.3 图片类、语音类——Media
package com.ray.pojo.message.resp; /**
* @desc : 图片、语音
*
* @author: shirayner
* @date : 2017-8-17 下午1:52:19
*/
public class Media { private String MediaId; public String getMediaId() {
return MediaId;
} public void setMediaId(String mediaId) {
MediaId = mediaId;
}
}
4.3.1 图片消息——ImageMessage
package com.ray.pojo.message.resp; /**
* @desc : 图片消息
*
* @author: shirayner
* @date : 2017-8-17 下午1:53:28
*/
public class ImageMessage extends BaseMessage { private Media Image; public Media getImage() {
return Image;
} public void setImage(Media image) {
Image = image;
}
}
4.3.2 语音消息——VoiceMessage
package com.ray.pojo.message.resp; /**
* @desc : 语音消息
*
* @author: shirayner
* @date : 2017-8-17 下午1:57:42
*/
public class VoiceMessage extends BaseMessage {
// 语音
private Media Voice; public Media getVoice() {
return Voice;
} public void setVoice(Media voice) {
Voice = voice;
} }
4.4 视频消息——VoiceMessage
package com.ray.pojo.message.resp; /**
* @desc : 语音消息
*
* @author: shirayner
* @date : 2017-8-17 下午1:57:42
*/
public class VoiceMessage extends BaseMessage {
// 语音
private Media Voice; public Media getVoice() {
return Voice;
} public void setVoice(Media voice) {
Voice = voice;
} }
4.5 图文消息——Article、NewsMessage
Article.java
package com.ray.pojo.message.resp; /**
* @desc : 图文
*
* @author: shirayner
* @date : 2017-8-17 下午2:02:33
*/
public class Article {
// 图文消息名称
private String Title;
// 图文消息描述
private String Description;
// 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80
private String PicUrl;
// 点击图文消息跳转链接
private String Url; public String getTitle() {
return Title;
} public void setTitle(String title) {
Title = title;
} public String getDescription() {
return null == Description ? "" : Description;
} public void setDescription(String description) {
Description = description;
} public String getPicUrl() {
return null == PicUrl ? "" : PicUrl;
} public void setPicUrl(String picUrl) {
PicUrl = picUrl;
} public String getUrl() {
return null == Url ? "" : Url;
} public void setUrl(String url) {
Url = url;
}
}
NewsMessage.java
package com.ray.pojo.message.resp; import java.util.List; /**
* @desc : 图文消息
*
* @author: shirayner
* @date : 2017-8-17 下午2:03:31
*/
public class NewsMessage extends BaseMessage {
// 图文消息个数,限制为10条以内
private int ArticleCount;
// 多条图文消息信息,默认第一个item为大图
private List<Article> Articles; public int getArticleCount() {
return ArticleCount;
} public void setArticleCount(int articleCount) {
ArticleCount = articleCount;
} public List<Article> getArticles() {
return Articles;
} public void setArticles(List<Article> articles) {
Articles = articles;
}
}
五、消息工具类——MessageUtil
package com.ray.util; import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; import com.ray.pojo.message.resp.Article;
import com.ray.pojo.message.resp.NewsMessage;
import com.ray.pojo.message.resp.TextMessage;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver; /**
* 消息工具类
* @author shirayner
*
*/
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"; /**
* @desc :1.解析微信发来的请求(XML),获取请求参数
*
* @param request
* @return
* @throws Exception Map<String,String>
*/
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;
} /**
* @desc :2.解析微信发来的请求(xmlStr),获取请求参数
*
* @param xmlStr
* @return
* @throws Exception Map<String,String>
*/
public static Map<String, String> parseXml(String xmlStr) throws Exception {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>(); //1.将字符串转为Document
Document document = DocumentHelper.parseText(xmlStr); //2.获取根元素的所有子节点
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements(); //3.遍历所有子节点
for (Element e : elementList)
map.put(e.getName(), e.getText()); return map;
} /**
* 2.文本消息对象转换成xml
*
* @param textMessage 文本消息对象
* @return xml
*/
public static String textMessageToXml(TextMessage textMessage) {
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
} /**
* 音乐消息对象转换成xml
*
* @param musicMessage 音乐消息对象
* @return xml
*/
/* public static String musicMessageToXml(MusicMessage musicMessage) {
xstream.alias("xml", musicMessage.getClass());
return xstream.toXML(musicMessage);
}
*/
/**
* 图文消息对象转换成xml
*
* @param newsMessage 图文消息对象
* @return xml
*/
public static String newsMessageToXml(NewsMessage newsMessage) {
xstream.alias("xml", newsMessage.getClass());
xstream.alias("item", new Article().getClass());
return xstream.toXML(newsMessage);
} /**
* 扩展xstream,使其支持CDATA块
*
* @date 2013-05-19
*/
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 对所有xml节点的转换都增加CDATA标记
boolean cdata = true; @SuppressWarnings("unchecked")
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);
}
}
};
}
});
}
六、消息业务类——MessageService
package com.ray.service; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.Map; import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest; import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.ray.pojo.message.resp.TextMessage;
import com.ray.util.MessageUtil;
import com.ray.util.WeiXinParamesUtil; /**
* @desc : 被动回复消息
*
* @author: shirayner
* @date : 2017-8-17 下午3:37:17
*/
public class MessageService { private String msg_signature ; // 微信加密签名
private String timestamp ; // 时间戳
private String nonce ; // 随机数 /**
* @desc :获取加密后的回复消息
*
* @param request
* @return String 返回加密后的回复消息
*/
public String getEncryptRespMessage(HttpServletRequest request){
String respMessage = null; try {
//1.解密微信发过来的消息
String xmlMsg=this.getDecryptMsg(request); //2.解析微信发来的请求,解析xml字符串
Map<String, String> requestMap= MessageUtil.parseXml(xmlMsg); //3.获取请求参数
//3.1 企业微信CorpID
String fromUserName = requestMap.get("FromUserName");
//3.2 成员UserID
String toUserName = requestMap.get("ToUserName");
//3.3 消息类型与事件
String msgType = requestMap.get("MsgType");
String eventType = requestMap.get("Event");
String eventKey = requestMap.get("EventKey");
System.out.println("msgType:"+msgType);
System.out.println("Event:"+eventType+" eventKey:"+eventKey); //4.组装 回复文本消息
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
//4.1.获取回复消息的内容 :消息的分类处理
String respContent=this.getRespContentByMsgType(msgType, eventType, eventKey);
textMessage.setContent(respContent);
System.out.println("respContent:"+respContent); //5.获取xml字符串: 将(被动回复消息型的)文本消息对象 转成 xml字符串
respMessage = MessageUtil.textMessageToXml(textMessage); //6.加密
WXBizMsgCrypt wxcpt=new WXBizMsgCrypt(WeiXinParamesUtil.token,WeiXinParamesUtil.encodingAESKey,WeiXinParamesUtil.corpId);
respMessage = wxcpt.EncryptMsg(respMessage, timestamp, msgType); } catch (Exception e) {
e.printStackTrace();
} return respMessage;
} /**
* @desc :2.从request中获取消息明文
*
* @param request
* @return String 消息明文
*/
public String getDecryptMsg(HttpServletRequest request) { String postData=""; // 密文,对应POST请求的数据
String result=""; // 明文,解密之后的结果 this.msg_signature = request.getParameter("msg_signature"); // 微信加密签名
this.timestamp = request.getParameter("timestamp"); // 时间戳
this.nonce = request.getParameter("nonce"); // 随机数 try {
//1.获取加密的请求消息:使用输入流获得加密请求消息postData
ServletInputStream in = request.getInputStream();
BufferedReader reader =new BufferedReader(new InputStreamReader(in)); String tempStr=""; //作为输出字符串的临时串,用于判断是否读取完毕
while(null!=(tempStr=reader.readLine())){
postData+=tempStr;
} //2.获取消息明文:对加密的请求消息进行解密获得明文
WXBizMsgCrypt wxcpt=new WXBizMsgCrypt(WeiXinParamesUtil.token,WeiXinParamesUtil.encodingAESKey,WeiXinParamesUtil.corpId);
result = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postData); } catch (IOException e) {
e.printStackTrace();
} catch (AesException e) {
e.printStackTrace();
} return result;
} /**
* @desc :3.处理消息:根据消息类型获取回复内容
*
* @param msgType
* @return String
*/
public String getRespContentByMsgType(String msgType,String eventType,String eventKey){
String respContent="";
//1.文本消息
if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
respContent = "您发送的是文本消息!"; }
//2.图片消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
respContent = "您发送的是图片消息!";
}
//3.地理位置消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
System.out.println("消息类型:定位");
}
//4.链接消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
respContent = "您发送的是链接消息!";
}
//5.音频消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
respContent = "您发送的是音频消息!";
}
//6.事件推送
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
respContent=this.processEevent(eventType, eventKey);
}
//7.请求异常
else {
respContent="请求处理异常,请稍候尝试!";
} return respContent;
} public String processEevent(String eventType,String eventKey){ String respContent="";
// 订阅
if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
respContent = "欢迎关注!";
}
// 取消订阅
else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
// TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
}
//上报地理位置事件
else if(eventType.equals("LOCATION")){ }
// 自定义菜单点击事件
else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) { if (eventKey.equals("12")) {
// TODO help
} else if (eventKey.equals("13")) {
respContent = "周边搜索菜单项被点击!";
} else if (eventKey.equals("14")) {
respContent = "历史上的今天菜单项被点击!";
} else if (eventKey.equals("21")) {
respContent = "歌曲点播菜单项被点击!";
} else if (eventKey.equals("22")) { respContent = "经典游戏菜单项被点击!";
} else if (eventKey.equals("23")) {
respContent = "美女电台菜单项被点击!";
} else if (eventKey.equals("24")) {
respContent = "人脸识别菜单项被点击!";
} else if (eventKey.equals("25")) {
respContent = "聊天唠嗑菜单项被点击!";
} else if (eventKey.equals("31")) {
respContent = "Q友圈菜单项被点击!";
} else if (eventKey.equals("32")) {
respContent = "电影排行榜菜单项被点击!";
} else if (eventKey.equals("33")) {
respContent = "幽默笑话菜单项被点击!";
}
}
return respContent;
} }
七、CoreServlet
package com.ray.servlet; import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.ray.service.MessageService;
import com.ray.util.WeiXinParamesUtil; /**
* 核心请求处理类
* @author shirayner
*
*/
public class CoreServlet extends HttpServlet {
private static final long serialVersionUID = 4440739483644821986L; /**
* 确认请求来自微信服务器
*/
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 微信加密签名
String msg_signature = request.getParameter("msg_signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr"); System.out.println("request=" + request.getRequestURL()); PrintWriter out = response.getWriter();
// 通过检验msg_signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
String result = null;
try {
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(WeiXinParamesUtil.token, WeiXinParamesUtil.encodingAESKey, WeiXinParamesUtil.corpId);
result = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr);
} catch (AesException e) {
e.printStackTrace();
}
if (result == null) {
result = WeiXinParamesUtil.token;
}
out.print(result);
out.close();
out = null;
} /**
* 处理微信服务器发来的消息
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8"); //2.调用消息业务类接收消息、处理消息
MessageService msgsv=new MessageService();
String respMessage = msgsv.getEncryptRespMessage(request); //处理表情
// String respMessage = CoreService.processRequest_emoj(request);
//处理图文消息
//String respMessage = Test_NewsService.processRequest(request); //3.响应消息
PrintWriter out = response.getWriter();
out.print(respMessage);
out.close();
} }
八、web.xml
注册servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <servlet>
<servlet-name>coreServlet</servlet-name>
<servlet-class>
com.ray.servlet.CoreServlet
</servlet-class>
</servlet> <servlet>
<servlet-name>uploadTempMaterialServlet</servlet-name>
<servlet-class>
com.ray.servlet.UploadTempMaterialServlet
</servlet-class>
</servlet> <servlet-mapping>
<servlet-name>uploadTempMaterialServlet</servlet-name>
<url-pattern>/uploadTempMaterialServlet</url-pattern>
</servlet-mapping> </web-app>
Java企业微信开发_05_消息推送之被动回复消息的更多相关文章
-
Java企业微信开发_05_消息推送之发送消息(主动)
一.本节要点 1.发送消息与被动回复消息 (1)流程不同:发送消息是第三方服务器主动通知微信服务器向用户发消息.而被动回复消息是 用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接 ...
-
Java企业微信开发_03_自定义菜单
一.本节要点 1.菜单相关实体类的封装 参考官方文档中的请求包的内容,对菜单相关实体类进行封装. 这里需要格外注意的是,企业微信中请求包的数据是Json字符串格式的,而不是xml格式.关于json序列 ...
-
Java企业微信开发_03_通讯录同步
一.本节要点 1.获取通讯录密钥 获取方式: 登录企业微信—>管理工具—>通讯录同步助手—>开启“API接口同步” ; 开启后,即可看到通讯录密钥,也可设置通讯录API的权限:读取 ...
-
Java企业微信开发_07_素材管理之上传本地临时素材文件
一.本节要点 1.临时素材有效期 media_id是可复用的,同一个media_id可用于消息的多次发送(3天内有效) 2.上传文件时的http请求里都有啥 具体原理可参看: 为什么上传文件的表单需要 ...
-
Java企业微信开发_06_素材管理之上传本地临时素材文件至微信服务器
一.本节要点 1.临时素材有效期 media_id是可复用的,同一个media_id可用于消息的多次发送(3天内有效) 2.上传文件时的http请求里都有啥 具体原理可参看: 为什么上传文件的表单需要 ...
-
Java企业微信开发_02_通讯录同步
一.本节要点 1.获取通讯录密钥 获取方式: 登录企业微信—>管理工具—>通讯录同步助手—>开启“API接口同步” ; 开启后,即可看到通讯录密钥,也可设置通讯录API的权限:读取 ...
-
Java企业微信开发_04_消息推送之发送消息(主动)
源码请见: Java企业微信开发_00_源码及资源汇总贴 一.本节要点 1.发送消息与被动回复消息 (1)流程不同:发送消息是第三方服务器主动通知微信服务器向用户发消息.而被动回复消息是 用户发送消息 ...
-
Java企业微信开发_01_接收消息服务器配置
一.准备阶段 需要准备事项: 1.一个能在公网*问的项目: 见:Java微信公众平台开发_01_本地服务器映射外网 2.一个企业微信账号: 去注册:(https://work.weixin.qq.c ...
-
Java企业微信开发_09_身份验证之移动端网页授权(有完整项目源码)
注: 源码已上传github: https://github.com/shirayner/WeiXin_QiYe_Demo 一.本节要点 1.1 授权回调域(可信域名) 在开始使用网页授权之前,需要先 ...
随机推荐
-
jcFeather Maya 羽毛插件
jcFeather 2.8.6 插件持续更新地址为:http://www.jerrykon.com/jcFeather.html 和 http://www.creativecrash.com/maya ...
-
编译器角度看C++复制构造函数
[C++对象模型]复制构造函数的建构操作 关于复制构造函数的简单介绍,可以看我以前写过的一篇文章C++复制控制之复制构造函数该文章中介绍了复制构造函数的定义.调用时机.也对编译器合成的复制构造函数行为 ...
-
Java 应用性能调优实践
Java 应用性能优化是一个老生常谈的话题,笔者根据个人经验,将 Java 性能优化分为 4 个层级:应用层.数据库层.框架层.JVM 层.通过介绍 Java 性能诊断工具和思路,给出搜狗商业平台的性 ...
-
linux学习笔记2:linux 下java开发的软件安装
一.java ee开发环境的搭建 1.jdk的安装步骤 (1)首先必须要有安装文件,具体的可以去相关网站上下载,并制作iso文件 (2)将制作的iso文件挂载到linux系统上,并在虚拟机上将iso文 ...
-
第二百四十天 how can I 坚持
在家待了一天,晚上出去买了个帽子,还有买了点排骨炖着吃了... 玩了好多局游戏. 想搞个直播,不知道能不能玩的起来. 水平太菜了,明天去小米之家玩玩. 睡觉.
-
Firebird/InterBase内置函数使用说明
Firebird/InterBase内置函数使用说明(转自:圣域天堂) 2008-10-12 20:56 加*号为FB2.0加入的函数 整理:剑雷(jianlei) 2006-10-13 1. COU ...
-
Java中的属性与字段的区别
Java中属性和字段的区别 Java中的属性,通常可以理解为其属名性时根据get和set方法名得出的. 其规则是:去掉get或set后其剩余的字符串,如果第二个字母是小写的,则把第一个字母也变成小写 ...
-
Linux系统守护进程详解ntsysv 可以关掉那些服务
acpid, haldaemon, messagebus, klogd,network, syslogd 以上几个服务必须开启!其他的分析如下: 1.NetworkManager,NetworkMa ...
-
CLR执行模型与常见的几个概念
CLR执行模型与常见的几个概念 一.CLR执行模型 1.1从代码到程序集 CLR执行的模型是从代码开始,经过编译器生成托管模块,默认情况下可以让多个托管模块和资源生成程序集.下图为其大致过程. 1.2 ...
-
无线路由WMM,Short GI,AP隔离功能介绍(转)
无线路由器中有开启WMM.开启Short GI和开启AP隔离分别代表什么呢?这是我在我的TP-LINK无线路由器TL-WR841N中的无线高级设置中看到的三个选项,下面三点对这三项无线高级设置做了下解 ...