<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

    http://fly-m.javaeye.com/blog/183534

    struts2亂碼與json插件(1)

        最近要用struts2做一個體育類的系統,由于系統本身的原因(要求可操作性強,用戶交互性高),所以不可避免地要用到ajax.在我的前一篇文章里已經提到了有關在struts2中運用ajax的一些東西了.這次重新再拾起來,仔細對比下其中的差別.
        在前一個例子性系統中,由于所有的編碼都是以UTF-8進行編碼的,所以也沒有碰到過有關亂碼的問題.普通調用和ajax調用都很正常地運行了.而在新的系統中,要求所有的頁面(包括數據庫)都要求運用GBK編碼.這樣,一些有關于亂碼的問題就出現了,亂碼...與struts2有關. 
        大家都知道,在struts2的配置文件中,有一個配置項直接跟編碼有關."struts.i18n.encoding"這個配置項表示struts2將對數據進行何種形式的編碼操作,默認的編碼為utf-8,這也是為什么在前一個新聞系統中沒有亂碼的原因,因為它本身都和系統默認編碼一致.而在新的系統中,由于要求必須將編碼改成GBK,配置如下:

     

    Xml代碼 復制代碼
    1. <constant name="struts.i18n.encoding" value="GBK"/>  

       在這種情況下,加上在tomcat serve.xml中修改URIEncoding=GBK,保證傳送到服務器的數據都是GBK格式.實際測試中,這種方式是正確的,編碼正確.
       然而好境不長,當運用到ajax時,問題出現了.我最先采用的是struts2的json插件,看它的要求,它要求工程編碼應該為UTF-8,而我的工程編碼實際為GBK,這是不可能改回去的事情.先不管它,在頁面中配置完成,進行ajax調用.果然,數據亂碼.在action中,對數據進行了測試,發現數據在進行action時已經是亂碼.對數據進行轉碼操作,發現將數據按

    Java代碼 復制代碼
    1. x = new String(x.getBytes("GBK"),"UTF-8");  

     時,數據正常了.這就表示數據是按照utf-8格式進行傳送的,而在sturts中可能將數據又轉回了gbk,導致亂碼產生.一開始從js入手,因為我用的prototype.js采用post進行傳送.設置的encoding為"UTF-8",修改js的encoding為"GBK",發現并沒有按照想像的方向發展,仍然亂碼.而采用get方式發送時,數據仍然是亂碼.
         只有從java方向入手了,處理方向有兩種,就像我在前面兩篇有關jsp亂碼中提到一樣.第一種是加攔截器,攔截到ajax請求時,將編碼重新轉碼操作.第二種就是像過濾器一樣,在進行參數編碼前設置正確的編碼.在struts2中采用的是第二種方法.其實struts.i18n.encoding采用的也是這種方法.相應的代碼如下:

    Java代碼 復制代碼
    1. public void prepare(HttpServletRequest request, HttpServletResponse response) {    
    2.         String encoding = null;    
    3.         if (defaultEncoding != null) {    
    4.             encoding = defaultEncoding;    
    5.         }   
    6.   
    7.         Locale locale = null;   
    8.   
    9.         if (defaultLocale != null) {   
    10.             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());   
    11.         }   
    12.         if (encoding != null) {    
    13.             try {    
    14.                 request.setCharacterEncoding(encoding);    
    15.             } catch (Exception e) {    
    16.                 LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);    
    17.             }   
    18.         }    
    19.         if (locale != null) {    
    20.             response.setLocale(locale);    
    21.         }    
    22.         if (paramsWorkaroundEnabled) {    
    23.             request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request    
    24.         }    
    25.     }  

     這個方法這是org.apache.struts2.dispatcher.Dispatcher中而被FilterDispatcher調用,后都大家都知道吧,是struts2的標準過濾器.它調用的地方如下:

    Java代碼 復制代碼
    1. protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {    
    2.     
    3.         Dispatcher du = Dispatcher.getInstance();    
    4.     
    5.         // Prepare and wrap the request if the cleanup filter hasn't already, cleanup filter should be    
    6.         // configured first before struts2 dispatcher filter, hence when its cleanup filter's turn,    
    7.         // static instance of Dispatcher should be null.    
    8.         if (du == null) {    
    9.     
    10.             Dispatcher.setInstance(dispatcher);    
    11.     
    12.             // prepare the request no matter what - this ensures that the proper character encoding    
    13.             // is used before invoking the mapper (see WW-9127)    
    14.             dispatcher.prepare(request, response);    
    15.         } else {    
    16.             dispatcher = du;    
    17.         }    
    18.             
    19.         try {    
    20.             // Wrap request first, just in case it is multipart/form-data    
    21.             // parameters might not be accessible through before encoding (ww-1278)    
    22.             request = dispatcher.wrapRequest(request, getServletContext());    
    23.         } catch (IOException e) {    
    24.             String message = "Could not wrap servlet request with MultipartRequestWrapper!";    
    25.             LOG.error(message, e);    
    26.             throw new ServletException(message, e);    
    27.         }    
    28.     
    29.         return request;    
    30.     }  

     由上可以看出,filter在實例化dispatcher后,調用其的prepare方法,而prepare方法中,好像涉及到的其他相關操作不多,只是操作request 和 response的,在prepare方法中,判斷encoding是不是空,如果不為空則將其設置編碼入request中.而在defaultEncoding的設置上,可以看出這個參數是跟struts.i18n.encoding相關的,相關代碼如下:

    Java代碼 復制代碼
    1. @Inject(StrutsConstants.STRUTS_I18N_ENCODING)    
    2.     public static void setEncoding(String val) {    
    3.         encoding = val;    
    4.     }  

     在上面這個方法中,將把struts.i18n.encoding注入到encoding中,也就是說,如果我們設置encoding為GBK,無論在何種條件下,它就是GBK編碼了.
          嘗試修改這種方式,因為直接影響的就是

    Java代碼 復制代碼
    1. prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response)   

    這個方法,于是直接修改如為如下:

    Java代碼 復制代碼
    1. public class TextFilter extends FilterDispatcher{   
    2.     private static final Log log = LogFactory.getLog(TextFilter.class);   
    3.     private FilterConfig filterConfig;   
    4.   
    5.     private static String defaultEncoding;   
    6.     private static String defaultLocale;   
    7.     private static String paramsWorkaroundEnabled = "false";   
    8.   
    9.     @Inject(org.apache.struts2.StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND)   
    10.     public static void setParamsWorkaroundEnabled(String enabled) {   
    11.         paramsWorkaroundEnabled = enabled;   
    12.     }   
    13.   
    14.     @Inject(StrutsConstants.STRUTS_I18N_ENCODING)   
    15.     public static void setEncoding(String encoding) {   
    16.         defaultEncoding = encoding;   
    17.     }   
    18.   
    19.     @Inject(value = StrutsConstants.STRUTS_LOCALE, required = false)   
    20.     public static void setLocale(String locale) {   
    21.         defaultLocale = locale;   
    22.     }   
    23.   
    24.     public void init(FilterConfig filterConfig) throws ServletException {   
    25.         super.init(filterConfig);   
    26.         this.filterConfig = filterConfig;   
    27.     }   
    28.   
    29.     protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {   
    30.         Dispatcher du = Dispatcher.getInstance();   
    31.   
    32.         if(du == null) {   
    33.             Dispatcher.setInstance(dispatcher);   
    34.             prepare(request, response);   
    35.         } else {   
    36.             dispatcher = du;   
    37.         }   
    38.   
    39.         try {   
    40.             request = dispatcher.wrapRequest(request, getServletContext());   
    41.         } catch(IOException e) {   
    42.             String message = "Could not wrap servlet request with MultipartRequestWrapper!";   
    43.             log.error(message, e);   
    44.             throw new ServletException(message, e);   
    45.         }   
    46.   
    47.         return request;   
    48.     }   
    49.   
    50.     private void prepare(HttpServletRequest request, HttpServletResponse response) {   
    51.         String encoding = request.getCharacterEncoding();   
    52.         if(encoding == null) {   
    53.             encoding = defaultEncoding;   
    54.         }   
    55.   
    56.         Locale locale = null;   
    57.         if(defaultLocale != null) {   
    58.             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());   
    59.         }   
    60.   
    61.         if(encoding != null) {   
    62.             try {   
    63.                 request.setCharacterEncoding(encoding);   
    64.             } catch(Exception e) {   
    65.                 log.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);   
    66.             }   
    67.         }   
    68.   
    69.         if(locale != null) {   
    70.             response.setLocale(locale);   
    71.         }   
    72.   
    73.         if(isParamsWorkaroundEnabled()) {   
    74.             request.getParameter("foo");   
    75.         }   
    76.     }   
    77.   
    78.     public boolean isParamsWorkaroundEnabled() {   
    79.         ServletContext servletContext = filterConfig.getServletContext();   
    80.         return servletContext != null && servletContext.getServerInfo() != null && servletContext.getServerInfo().indexOf("WebLogic") >= 0 ||   
    81.                 paramsWorkaroundEnabled.equalsIgnoreCase("true");   
    82.     }   
    83. }   
    84.    

     可以看出,整個就是繼承filterDispatcher,再修改其中的一個方法.將dispatcher.prepare()這一名改成filter.prepare的形式,實際上效果是一樣的.惟一讓人感覺不爽的就是,由于在filter中設置的各種變量都是私有而且是是靜態的(我嘗試用反射都不能得到它的值),導致直接不能得到父類的屬性值,沒辦法,只有再加變量了.在上面的prepare中,判斷request中的編碼是不是為空,一般來說,從jsp頁面傳送的編碼都是空的.當由js進行傳送時,由于已經設置傳送編碼為UTF-8,故getCharacterEncoding()不為空,則不再進行設置編碼了.其他由設置傳送編碼為defaultEncoding(即sturts.i18n.encoding).
    在上面的有一句

    Java代碼 復制代碼
    1. if(isParamsWorkaroundEnabled()) {           request.getParameter("foo");        }  

    專為weblogic設置,由于沒有接觸過,略過.值得不提的是,request.getParamter("foo").這一句,在tomcat里面是直接將編碼固定化,即只要調用了這一句,都將使得request(在tomcat的實現中)不能再接受其他編碼(參數已經被轉化了).
    最后,將filter設置在struts.xml中,以便窗口將參數@inject注入到filter中.

    Java代碼 復制代碼
    1. <bean class="m_ylf.cs.sicau.struts2.TextFilter" static="true"/>  

     上面一句必須要,不然相應的靜態參數都沒有被注入,是會產生NullPointerException的哦.
    先解決這一個問題,下一個問題將介紹struts2的json插件,及改進方法.

    struts2與json插件(2)

     

    在前一篇中<struts2與json插件(1)> ,講到了解決在struts2中出現的一點點亂碼問題,就想看看json中的數據處理方式.由struts2的處理流程來看,主要處理result的代碼如下在defaultActionInvocation中:

    Java代碼 復制代碼
    1. private void executeResult() throws Exception {   
    2.         result = createResult();   
    3.   
    4.         String timerKey = "executeResult: "+getResultCode();   
    5.         try {   
    6.             UtilTimerStack.push(timerKey);   
    7.             if (result != null) {   
    8.                 result.execute(this);   
    9.             } else if (resultCode != null && !Action.NONE.equals(resultCode)) {   
    10.                 throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()    
    11.                         + " and result " + getResultCode(), proxy.getConfig());   
    12.             } else {   
    13.                 if (LOG.isDebugEnabled()) {   
    14.                     LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());   
    15.                 }   
    16.             }   
    17.         } finally {   
    18.             UtilTimerStack.pop(timerKey);   
    19.         }   
    20.     }  

     
    如上所示,result就是返回的result,而resultCode就是我們通用的String返回類型了.而json插件中,采用默認值Action.Success來得到json類型,而在result中處理,即調用上面的result.invoke(this).json中的主要處理代碼如下(省略中間一些代碼):

    Java代碼 復制代碼
    1. public void execute(ActionInvocation invocation) throws Exception {   
    2.     ActionContext actionContext = invocation.getInvocationContext();   
    3.     HttpServletRequest request = (HttpServletRequest) actionContext   
    4.         .get(StrutsStatics.HTTP_REQUEST);   
    5.     HttpServletResponse response = (HttpServletResponse) actionContext   
    6.         .get(StrutsStatics.HTTP_RESPONSE);   
    7.   
    8.     try {   
    9.         String json;   
    10.         Object rootObject;   
    11.         if (this.enableSMD) {   
    12.             //generate SMD   
    13.             rootObject = this.writeSMD(invocation);   
    14.         } else {   
    15.             // generate JSON   
    16.             if (this.root != null) {   
    17.                 ValueStack stack = invocation.getStack();   
    18.                 rootObject = stack.findValue(this.root);   
    19.             } else {   
    20.                 rootObject = invocation.getAction();   
    21.             }   
    22.         }   
    23.         json = JSONUtil   
    24.             .serialize(rootObject, this.excludeProperties, ignoreHierarchy, enumAsBean);   
    25.   
    26.         boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);   
    27.           
    28.         JSONUtil.writeJSONToResponse(response, this.defaultEncoding,   
    29.             isWrapWithComments(), json, false, writeGzip);   
    30.   
    31.     } catch (IOException exception) {   
    32.         log.error(exception);   
    33.         throw exception;   
    34.     }   
    35. }  

     

    可以看出,json插件的功能(在處理json上),就是將invocation中的action對象轉化成json對象,再輸出到頁面上.
        在上面的應用上來看,用json插件必須注冊json的resultType,而且,返回類型一定是json,也就是說,你必須給每一個json調用定義一個json的返回類型,而實際上這個類型就是轉向jsonResult上,和一般的調用不一樣,它不返回任何視圖.即json這個返回類型感覺上完全是多余的,除了標明是json返回之外沒有其他作用.而且對于解析來說,它是解析一個action對象,所就是說,如果你的action是富對象,里面有很多的屬性,而實際你需要的不多時,那這個解析就完全失去了作用了.
         話說這么多,改進方法,從根本上來說,就是改進這種解析機制.是不解析當前的action對象,而是像spring一樣,解析只需要解析的對象一樣.如返回a,就只需要解析a.查看上面的executeResult()方法,其中對result和resultCode進行了處理.其中有這么一句

    Java代碼 復制代碼
    1. if (result != null) {   
    2.                 result.execute(this);   
    3.             } else if (resultCode != null && !Action.NONE.equals(resultCode)) {   
    4.                 throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()    
    5.                         + " and result " + getResultCode(), proxy.getConfig());   
    6.             } else {   
    7.                 if (LOG.isDebugEnabled()) {   
    8.                     LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());   
    9.                 }   
    10.             }  

     

    第一句,也就是一般我們進行處理的地方,調用result進行最后的處理.而第二句,則是對resultCode進行判斷,而第三句什么都不做.所以,對于其他的處理,可以從resultCode下手.方法有兩種,一是返回無類型,即void類型,二是返回Action.NONE.當是這兩種類型的時候,struts2就不會對result進行主動處理了.詳細可參見struts2對Action.NONE的說明,如下:


    Java代碼 復制代碼
    1. /**  
    2.      * The action execution was successful but do not  
    3.      * show a view. This is useful for actions that are  
    4.      * handling the view in another fashion like redirect.  
    5.      */  
    6.     public static final String NONE = "none";  

     

     

     即我們只需要在action方法中,處理ajax調用,而返回void或者"none"就行了.參考方法調用就如下:


    Java代碼 復制代碼
    1. public void showCategoryListAjax2() throws Exception {   
    2.         this.category = this.service.getCategory(this.id);   
    3.         /** 如果該類別為空 **/  
    4.         if(this.category == null) {   
    5.             AjaxResponse.sendAjaxText(null, ServletActionContext.getResponse());   
    6.             return;   
    7.         }   
    8.   
    9.         this.categoryList = this.service.getCategoryListBySuperCategory(this.category);   
    10.   
    11.         AjaxResponse.sendAjaxText(this.categoryList, ServletActionContext.getResponse());   
    12.     }  

     

     

     上面的返回類別是一種取巧的方法,更好的方法是返回Action.NONE.這樣在struts.xml配置就像這樣:

    Java代碼 復制代碼
    1. <action name="taa" class="articleAction" method="topArticleAjax"/>  

     是不是更好,惟一要做的就是在action方法處理json轉換.不過這不是主要問題,在參考json的轉換模式上,我對json轉換進行了改進.在Fly_m的注解上如下:

    Java代碼 復制代碼
    1. public @Interface Fly_m{   
    2.       String name() default "";    
    3.       String format() default "";    
    4.       boolean exclude() default false;    
    5. }  

     支持如json一樣的名稱和format以及exclude(如果為真則不進行轉換).另外,支持排除指定類(如果轉換對象類型和排除類一致而不進行轉換)和指定名稱的對象(如果對象名稱和排除對象名稱一致,則被忽略,只限于對象和map.這對于hibernate這種相互調用的持久層對象最好了).如對象a.b.c的形式.默認的json轉換是完全轉換,在json插件上,如果在b上設定json(exclude),則無論如何b都不會被轉換,這種方法太過固定,不支持臨時配置.而在改進中,只需要加一個"a.b"形式的excludeProperties參數就可以了.如果想轉換時,把參數去掉就行了.相應的轉換方法如下:

    Java代碼 復制代碼
    1. public static String convertToJson(Object obj, String... excludeProperties) {   
    2.         return convertToJson(obj, nullnull, excludeProperties);   
    3.     }  

      

    Java代碼 復制代碼
    1. public static String convertToJson(Object obj, Format format, Class[] excludeClasses, String... excludeProperties) {   
    2.         JsonHandle jsonHandle = new JsonHandle();   
    3.         jsonHandle.excludeProperties = excludeProperties;   
    4.         jsonHandle.excludeClasses = excludeClasses;   
    5.         if(format != null)   
    6.             jsonHandle.defaultFormat = format;   
    7.         return jsonHandle.convertToJson(null0, obj);   
    8.     }  

      而在jsonHandle中是這樣處理的.

    Java代碼 復制代碼
    1. public String convertToJson(String s, int depth, Object obj) {   
    2.             if(obj == null || obj.getClass().getName().indexOf("$$EnhancerByCGLIB$$") != -1 || contains(excludeClasses, obj.getClass())) {   
    3.                 sb.append("null");   
    4.             } else if(isSimpleType(obj.getClass())) {   
    5.                 if(obj instanceof Character) {   
    6.                     sb.append("'").append(obj).append("'");   
    7.                 } else {   
    8.                     sb.append(obj);   
    9.                 }   
    10.             } else  
    11.             if(String.class.isAssignableFrom(obj.getClass()) || StringBuffer.class.isAssignableFrom(obj.getClass())   
    12.                     || StringBuilder.class.isAssignableFrom(obj.getClass())) {   
    13.                 sb.append('"');   
    14.                 CharacterIterator it = new StringCharacterIterator(obj.toString());   
    15.   
    16.                 for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {   
    17.                     if(c == '"') {   
    18.                         sb.append("\\\"");   
    19.                     } else if(c == '\\') {   
    20.                         sb.append("\\\\");   
    21.                     } else if(c == '/') {   
    22.                         sb.append("\\/");   
    23.                     } else if(c == '\b') {   
    24.                         sb.append("\\b");   
    25.                     } else if(c == '\f') {   
    26.                         sb.append("\\f");   
    27.                     } else if(c == '\n') {   
    28.                         sb.append("\\n");   
    29.                     } else if(c == '\r') {   
    30.                         sb.append("\\r");   
    31.                     } else if(c == '\t') {   
    32.                         sb.append("\\t");   
    33.                     } else if(Character.isISOControl(c)) {   
    34.                         sb.append(unicode(c));   
    35.                     } else {   
    36.                         sb.append(c);   
    37.                     }   
    38.                 }   
    39.   
    40.                 sb.append('"');   
    41.             } else if(obj instanceof Collection) {   
    42.                 boolean hibernateFlag;   
    43.                 try {   
    44.                     ((Collection) obj).size();   
    45.                     hibernateFlag = true;   
    46.                 } catch(Exception ex) {   
    47.                     hibernateFlag = false;   
    48.                 }   
    49.   
    50.                 if(hibernateFlag) {   
    51.                     sb.append("[");   
    52.   
    53.                     for(Iterator iterator = ((Collection) obj).iterator(); iterator.hasNext();) {   
    54.                         convertToJson(s, depth, iterator.next());   
    55.   
    56.                         if(iterator.hasNext()) {   
    57.                             sb.append(",\n");   
    58.                         }   
    59.                     }   
    60.   
    61.                     sb.append("]");   
    62.                 } else {   
    63.                     sb.append("null");   
    64.                 }   
    65.   
    66.             } else if(obj.getClass().isArray()) {   
    67.                 sb.append("[");   
    68.   
    69.                 int max = java.lang.reflect.Array.getLength(obj);   
    70.   
    71.                 for(int i = 0; i < max; i++) {   
    72.                     if(i > 0) {   
    73.                         sb.append(",");   
    74.                     }   
    75.                     convertToJson(s, depth, java.lang.reflect.Array.get(obj, i));   
    76.                 }   
    77.   
    78.                 sb.append("]");   
    79.             } else if(java.util.Map.class.isAssignableFrom(obj.getClass())) {   
    80.                 if(sb.length() > 0 && sb.lastIndexOf(",") != -1) {   
    81.                     sb.insert(sb.lastIndexOf(",") + 1"\n");   
    82.                 }   
    83.                 sb.append("{");   
    84.   
    85.                 for(Map.Entry e : ((Map<?, ?>) obj).entrySet()) {   
    86.                     if(!(e.getKey() instanceof String))   
    87.                         continue;   
    88.                     if(contains(excludeProperties, e.getKey().toString())) {   
    89.                         continue;   
    90.                     }   
    91.                     if(sb.length() > 0 && sb.charAt(sb.length() - 1) == ',' && sb.charAt(sb.length() - 2) == '}') {   
    92.                         sb.insert(sb.length(), "\n");   
    93.                     }   
    94.                     sb.append("\"").append(e.getKey()).append("\"").append(":");   
    95.   
    96.                     if(depth <= DEFAULT_DEPTH) {   
    97.                         convertToJson(add(s, e.getKey().toString()), depth + 1, e.getValue());   
    98.                     } else {   
    99.                         sb.append("undefined");   
    100.                     }   
    101.   
    102.                     sb.append(",");   
    103.                 }   
    104.   
    105.                 if(sb.length() > 3) {   
    106.                     sb.deleteCharAt(sb.length() - 1);   
    107.                 }   
    108.   
    109.                 sb.append("}");   
    110.             } else {   
    111.                 Map map = null;   
    112.                 try {   
    113.                     map = getPropertiesByReflect(this, obj);   
    114.                 } catch(Exception ex) {   
    115.                     ex.printStackTrace();   
    116.                 }   
    117.   
    118.                 convertToJson(s, depth, map);   
    119.             }   
    120.             return sb.toString();   
    121.         }   
    122.     }  

     

     相關的轉換方法都參照了其他的處理方式,具體解析方法請參照附件中的JsonUtils類.

     這篇文章只是說明了一種對于json的一種新的處理方式,并不是對于json插件的不滿,當然我還是喜歡自己的處理方式.加上前面對亂碼的處理,算是對struts2的一種補充吧.

    • JsonUtil.rar (2.1 KB)
    • 描述: JsonUtils.java 源代碼.
    • 下載次數: 53
    posted on 2008-11-20 17:04 蘆葦 閱讀(3034) 評論(0)  編輯  收藏 所屬分類: Struts
    主站蜘蛛池模板: 国产片免费在线观看| 亚洲在成人网在线看| 亚洲精品成人片在线观看| 日韩在线天堂免费观看| 在线精品免费视频| 日本a级片免费看| 成人永久福利免费观看| 国产成人3p视频免费观看| 免费永久在线观看黄网站| www.亚洲一区| 久久久久亚洲精品中文字幕| 在线观看国产区亚洲一区成人 | 偷自拍亚洲视频在线观看99| 在线观看国产一区亚洲bd| 风间由美在线亚洲一区| 免费国产在线精品一区| 岛国岛国免费V片在线观看| 国产一级淫片a免费播放口| 91精品全国免费观看含羞草| 亚洲免费在线观看视频| 成人毛片18女人毛片免费| 国产国产成年年人免费看片| 亚洲成年人啊啊aa在线观看| 亚洲日韩中文无码久久| 亚洲国产精品久久久久婷婷老年| 亚洲成在人线电影天堂色| 亚洲熟妇AV一区二区三区宅男| 国产亚洲精品仙踪林在线播放| 一区二区三区免费视频网站| 国产免费拔擦拔擦8X高清在线人| 亚洲黄色免费网站| 暖暖日本免费在线视频| 国产亚洲精品AA片在线观看不加载 | 亚洲AV成人精品日韩一区| 一区二区三区免费看| 久久久久久久岛国免费播放| 青青草a免费线观a| 午夜亚洲av永久无码精品| 久久亚洲精品成人| 久久久久久亚洲精品影院| 一级做a爱过程免费视|