1. 程式人生 > >利用支付寶的團隊編寫的 HttpProtocolHandler 、StringRequestEntity 呼叫微信 支付引發的 body不是UTF8編碼的

利用支付寶的團隊編寫的 HttpProtocolHandler 、StringRequestEntity 呼叫微信 支付引發的 body不是UTF8編碼的

HttpProtocolHandler 程式碼修改後的程式碼如下

package com.whb.common.util.httpclient;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;
import org.apache.http.entity.StringEntity;
import org.apache.log4j.Logger;

import com.whb.common.util.Util;

/* *
 *類名:HttpProtocolHandler
 *功能:HttpClient方式訪問
 *詳細:獲取遠端HTTP資料
 *版本:3.3
 *日期:2012-08-17
 *說明:
 *以下程式碼只是為了方便商戶測試而提供的樣例程式碼,商戶可以根據自己網站的需要,按照技術文件編寫,並非一定要使用該程式碼。
 *該程式碼僅供學習和研究支付寶介面使用,只是提供一個參考。
 */

public class HttpProtocolHandler {

	private static Logger log = Logger.getLogger(HttpProtocolHandler.class); 
	
    private static String              DEFAULT_CHARSET                     = "utf-8";

    /** 連線超時時間,由bean factory設定,預設為8秒鐘 */
    private int                        defaultConnectionTimeout            = 8000;

    /** 迴應超時時間, 由bean factory設定,預設為30秒鐘 */
    private int                        defaultSoTimeout                    = 30000;

    /** 閒置連線超時時間, 由bean factory設定,預設為60秒鐘 */
    private int                        defaultIdleConnTimeout              = 60000;

    private int                        defaultMaxConnPerHost               = 30;

    private int                        defaultMaxTotalConn                 = 80;

    /** 預設等待HttpConnectionManager返回連線超時(只有在達到最大連線數時起作用):1秒*/
    private static final long          defaultHttpConnectionManagerTimeout = 3 * 1000;

    /**
     * HTTP連線管理器,該連線管理器必須是執行緒安全的.
     */
    private HttpConnectionManager      connectionManager;

    private static HttpProtocolHandler httpProtocolHandler                 = new HttpProtocolHandler();

    /**
     * 工廠方法
     * 
     * @return
     */
    public static HttpProtocolHandler getInstance() {
        return httpProtocolHandler;
    }

    /**
     * 私有的構造方法
     */
    private HttpProtocolHandler() {
        // 建立一個執行緒安全的HTTP連線池
        connectionManager = new MultiThreadedHttpConnectionManager();
        connectionManager.getParams().setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);
        connectionManager.getParams().setMaxTotalConnections(defaultMaxTotalConn);
        IdleConnectionTimeoutThread ict = new IdleConnectionTimeoutThread();
        ict.addConnectionManager(connectionManager);
        ict.setConnectionTimeout(defaultIdleConnTimeout);

        ict.start();
        
    }

    /**
     * 執行Http請求
     * 
     * @param request 請求資料
     * @param strParaFileName 檔案型別的引數名
     * @param strFilePath 檔案路徑
     * @return 
     * @throws HttpException, IOException 
     */
    public HttpResponse execute(HttpRequest request, String strParaFileName, String strFilePath) throws HttpException, IOException {
        HttpClient httpclient = new HttpClient(connectionManager);       
        
        // 設定連線超時
        int connectionTimeout = defaultConnectionTimeout;
        if (request.getConnectionTimeout() > 0) {
            connectionTimeout = request.getConnectionTimeout();
        }
        httpclient.getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);

        // 設定迴應超時
        int soTimeout = defaultSoTimeout;
        if (request.getTimeout() > 0) {
            soTimeout = request.getTimeout();
        }
        httpclient.getHttpConnectionManager().getParams().setSoTimeout(soTimeout);

        // 設定等待ConnectionManager釋放connection的時間
        httpclient.getParams().setConnectionManagerTimeout(defaultHttpConnectionManagerTimeout);

        String charset = request.getCharset();
        charset = charset == null ? DEFAULT_CHARSET : charset;
        HttpMethod method = null;

        //get模式且不帶上傳檔案
        if (request.getMethod().equals(HttpRequest.METHOD_GET)) {
            method = new GetMethod(request.getUrl());
            method.getParams().setCredentialCharset(charset);

            // parseNotifyConfig會保證使用GET方法時,request一定使用QueryString
            method.setQueryString(request.getQueryString());
        } else if(strParaFileName.equals("") && strFilePath.equals("")) {
        	//post模式且不帶上傳檔案
            method = new PostMethod(request.getUrl());
            
            ((PostMethod) method).addParameters(request.getParameters());
            method.addRequestHeader("Content-Type", "application/x-www-form-urlencoded; text/html; charset=" + charset);
            
            
            String body=request.getBody();
            
            /**
             * 如果沒有這樣設定 預設編碼格式是 ISO-8859-1 向微信傳送請求會有問題
             */
            method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
            if(!Util.isEmpty(body))
            	((PostMethod) method).setRequestEntity(new StringRequestEntity(body,"text/html",charset));
        }
        else {
        	//post模式且帶上傳檔案
            method = new PostMethod(request.getUrl());
            List<Part> parts = new ArrayList<Part>();
            for (int i = 0; i < request.getParameters().length; i++) {
            	parts.add(new StringPart(request.getParameters()[i].getName(), request.getParameters()[i].getValue(), charset));
            }
            //增加檔案引數,strParaFileName是引數名,使用本地檔案
            parts.add(new FilePart(strParaFileName, new FilePartSource(new File(strFilePath))));
            
            // 設定請求體
            ((PostMethod) method).setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[0]), new HttpMethodParams()));
        }

        // 設定Http Header中的User-Agent屬性
        method.addRequestHeader("User-Agent", "Mozilla/4.0");
        HttpResponse response = new HttpResponse();

        try {
            httpclient.executeMethod(method);
            if (request.getResultType().equals(HttpResultType.STRING)) {
                response.setStringResult(method.getResponseBodyAsString());
            } else if (request.getResultType().equals(HttpResultType.BYTES)) {
                response.setByteResult(method.getResponseBody());
            }
            response.setResponseHeaders(method.getResponseHeaders());
        } catch (UnknownHostException ex) {

            return null;
        } catch (IOException ex) {

            return null;
        } catch (Exception ex) {

            return null;
        } finally {
            method.releaseConnection();
        }
        return response;
    }

    /**
     * 將NameValuePairs陣列轉變為字串
     * 
     * @param nameValues
     * @return
     */
    protected String toString(NameValuePair[] nameValues) {
        if (nameValues == null || nameValues.length == 0) {
            return "null";
        }

        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < nameValues.length; i++) {
            NameValuePair nameValue = nameValues[i];

            if (i == 0) {
                buffer.append(nameValue.getName() + "=" + nameValue.getValue());
            } else {
                buffer.append("&" + nameValue.getName() + "=" + nameValue.getValue());
            }
        }

        return buffer.toString();
    }
}
修改過的程式碼:

原先不支援 Post for body 後來 我加了 如下 ,如下程式碼 會引發微信 支付引發的 body不是UTF8編碼的,但如果用Main傳送請求是正常的

            method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
            if(!Util.isEmpty(body))
            	((PostMethod) method).setRequestEntity(new StringRequestEntity(body));

但對 StringRequestEntity 用的不多,後來修改如下:
            method.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, charset);
            if(!Util.isEmpty(body))
            	((PostMethod) method).setRequestEntity(new StringRequestEntity(body,"text/html",charset));