轉載自:http://blog.csdn.net/wangqianjiao/article/details/8469780
微信公眾平臺現在推出自動回覆訊息介面,但是由於是介面內容用的是PHP語言寫的,很多地方操作起來讓本人這個對java比較熟悉的小夥很彆扭,所以仿照PHP的介面程式碼做了一套jsp語言編寫的介面。
首先先把整個介面程式碼貼出來做下比較,然後我們再分析程式碼:
PHP程式碼:
<?php
/**
* wechat php test
*/
//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
$wechatObj->valid();
class wechatCallbackapiTest
{
public function valid()
{
$echoStr = $_GET["echostr"];
//valid signature , option
if($this->checkSignature()){
echo $echoStr;
exit;
}
}
public function responseMsg()
{
//get post data, May be due to the different environments
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
//extract post data
if (!empty($postStr)){
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$fromUsername = $postObj->FromUserName;
$toUsername = $postObj->ToUserName;
$keyword = trim($postObj->Content);
$time = time();
$textTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0</FuncFlag>
</xml>";
if(!empty( $keyword ))
{
$msgType = "text";
$contentStr = "Welcome to wechat world!";
$resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
echo $resultStr;
}else{
echo "Input something...";
}
}else {
echo "";
exit;
}
}
private function checkSignature()
{
$signature = $_GET["signature"];
$timestamp = $_GET["timestamp"];
$nonce = $_GET["nonce"];
$token = TOKEN;
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
}
?>
JAVA程式碼:
<%@page import="java.util.Date"%>
<%@page import="org.dom4j.Element"%>
<%@page import="org.dom4j.DocumentHelper"%>
<%@page import="org.dom4j.Document"%>
<%@page import="java.io.IOException"%>
<%@page import="java.io.InputStreamReader"%>
<%@page import="java.io.BufferedReader"%>
<%@page import="java.io.Reader"%>
<%@page import="java.security.MessageDigest"%>
<%@page import="java.util.Arrays"%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
//WeiXinHandler為內部類不能使用非final型別的物件
final String TOKEN="weixin";
final HttpServletRequest final_request=request;
final HttpServletResponse final_response=response;
%>
<%
class WeiXinHandler{
public void valid(){
String echostr=final_request.getParameter("echostr");
if(null==echostr||echostr.isEmpty()){
responseMsg();
}else{
if(this.checkSignature()){
this.print(echostr);
}else{
this.print("error");
}
}
}
//自動回覆內容
public void responseMsg(){
String postStr=null;
try{
postStr=this.readStreamParameter(final_request.getInputStream());
}catch(Exception e){
e.printStackTrace();
}
//System.out.println(postStr);
if (null!=postStr&&!postStr.isEmpty()){
Document document=null;
try{
document = DocumentHelper.parseText(postStr);
}catch(Exception e){
e.printStackTrace();
}
if(null==document){
this.print("");
return;
}
Element root=document.getRootElement();
String fromUsername = root.elementText("FromUserName");
String toUsername = root.elementText("ToUserName");
String keyword = root.elementTextTrim("Content");
String time = new Date().getTime()+"";
String textTpl = "<xml>"+
"<ToUserName><![CDATA[%1$s]]></ToUserName>"+
"<FromUserName><![CDATA[%2$s]]></FromUserName>"+
"<CreateTime>%3$s</CreateTime>"+
"<MsgType><![CDATA[%4$s]]></MsgType>"+
"<Content><![CDATA[%5$s]]></Content>"+
"<FuncFlag>0</FuncFlag>"+
"</xml>";
if(null!=keyword&&!keyword.equals(""))
{
String msgType = "text";
String contentStr = "Welcome to wechat world!";
String resultStr = textTpl.format(textTpl, fromUsername, toUsername, time, msgType, contentStr);
this.print(resultStr);
}else{
this.print("Input something...");
}
}else {
this.print("");
}
}
//微信介面驗證
public boolean checkSignature(){
String signature = final_request.getParameter("signature");
String timestamp = final_request.getParameter("timestamp");
String nonce = final_request.getParameter("nonce");
String token=TOKEN;
String[] tmpArr={token,timestamp,nonce};
Arrays.sort(tmpArr);
String tmpStr=this.ArrayToString(tmpArr);
tmpStr=this.SHA1Encode(tmpStr);
if(tmpStr.equalsIgnoreCase(signature)){
return true;
}else{
return false;
}
}
//向請求端傳送返回資料
public void print(String content){
try{
final_response.getWriter().print(content);
final_response.getWriter().flush();
final_response.getWriter().close();
}catch(Exception e){
}
}
//陣列轉字串
public String ArrayToString(String [] arr){
StringBuffer bf = new StringBuffer();
for(int i = 0; i < arr.length; i++){
bf.append(arr[i]);
}
return bf.toString();
}
//sha1加密
public String SHA1Encode(String sourceString) {
String resultString = null;
try {
resultString = new String(sourceString);
MessageDigest md = MessageDigest.getInstance("SHA-1");
resultString = byte2hexString(md.digest(resultString.getBytes()));
} catch (Exception ex) {
}
return resultString;
}
public final String byte2hexString(byte[] bytes) {
StringBuffer buf = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
if (((int) bytes[i] & 0xff) < 0x10) {
buf.append("0");
}
buf.append(Long.toString((int) bytes[i] & 0xff, 16));
}
return buf.toString().toUpperCase();
}
//從輸入流讀取post引數
public String readStreamParameter(ServletInputStream in){
StringBuilder buffer = new StringBuilder();
BufferedReader reader=null;
try{
reader = new BufferedReader(new InputStreamReader(in));
String line=null;
while((line = reader.readLine())!=null){
buffer.append(line);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(null!=reader){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return buffer.toString();
}
}
%>
<%
WeiXinHandler handler=new WeiXinHandler();
handler.valid();
%>
以上就是PHP介面和JSP介面的所有程式碼,現在我們來對一些需要注意的地方做下分析:
首先的從總體看的話,jsp要比PHP繁瑣一些,因為很多函式需要自己寫,像sha1加密,解析xml字串等都需要自己找第三方的庫。
第一點,我們要獲取微信公眾平臺給jsp傳送的post或get引數,正常情況下都是用request.getParameter就可以獲取到,但是在寫的過程中發現PHP是這樣獲取
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
這時通過查詢一些資料知道這樣獲取的是無法通過$_GET或$_POST函式得到的”未識別 MIME 型別的資料“,原始的 POST 資料
(參考:http://blog.csdn.net/china_skag/article/details/7284227)
所以這裡使用獲取原始資料流的方式來解析post的xml資料
String postStr=null;
try{
postStr=this.readStreamParameter(final_request.getInputStream());
}catch(Exception e){
e.printStackTrace();
}
//從輸入流讀取post引數
public String readStreamParameter(ServletInputStream in){
StringBuilder buffer = new StringBuilder();
BufferedReader reader=null;
try{
reader = new BufferedReader(new InputStreamReader(in));
String line=null;
while((line = reader.readLine())!=null){
buffer.append(line);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(null!=reader){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return buffer.toString();
}
第二個,是response訊息返回給微信平臺,我嘗試的用最一般的out.print去做,但是發現沒反應,觀察PHP的程式碼寫法
echo "";
exit;
猜想可能需要有個重新整理的操作才能把訊息response回去,於是找了下response內的一些函式做出以下嘗試
//向請求端傳送返回資料
public void print(String content){
try{
final_response.getWriter().print(content);
final_response.getWriter().flush();
final_response.getWriter().close();
}catch(Exception e){
}
}
發現以上做法是可以在微信傳送端得到訊息的;
第三個,介面描述上說目前只支援80埠的服務端地址,所以我這裡的做法是用apache伺服器路由到tomcat的jsp上
關於微信公眾平臺的訊息介面的詳細介紹,可以參看微信公眾平臺的官方文件,裡面介紹了訊息的xml的格式和訊息的傳送方式等。