[ SSH框架 ] Struts2框架學習之二

一、Struts2訪問Servlet的API

  前面已經對 Struts2的流程已經執行完成了,但是如果表單中有參數如何進行接收又或者我們需要向頁面保存一些數據,又要如何完成呢?我們可以通過學習 Struts2訪問 Servlet的API來實現這樣的功能。

  在 Struts2中, Action並沒有直接和 Servlet api進行耦合,也就是說在 Struts2的 Action中不能直接訪問 Servlet api。雖然 Struts2中的 Action訪問 Servlet API麻煩一些,但是這卻是 Struts2中 Action的重要改良之一,方便 Action進行單元測試。

  儘管 Action和 Servlet api解耦會帶來很多好處,然而在 Action中完全不訪問 Servlet Api幾乎是不可能的,在實現業務邏輯時,經常要訪問 Servlet中的對象,如 session、 request和 application等。

  在 Struts2中,訪問 Servlet API有3種方法,具體如下:

1.1 通過ActionContext訪問

  Struts2框架提供了 Action Context類來訪問 Servlet API。Action Context是 Action執行的上下文對象,在 Action Context中保存了 Action執行所需要的所有對象,包括 parameters, request, session,application等。下面列舉 Action context類訪問 Servlet apl的幾個常用方法,具體如表所示。

以上列舉的是 Action context類訪問 Servlet apl的常用方法,要訪問 Servlet api,可以通過如下方式進行,具體示例代碼如下

    ActionContext context=ActionContext.getContext();
    context.put("name", "Kevin");
    context.getApplication().put("name", "Kevin");
    context.getSession().put("name", "Kevin"); 

在上述示例代碼中,通過 ActionContext類中的方法調用,分別在 request、 application和 session中放入了("name"," Kevin")對。可以看到,通過 ActionContext類可以非常簡單地訪問JSP內置對象的屬性。

  爲了讓大家更好地掌握如何通過 Action Context類來訪問 Servlet API,接下來通過一個具體的案例來演示 Actioncontext的使用:

(1)在 Eclipse中創建一個名稱爲 struts2dayo2的web項目,將 Struts2所需的jar包複製到Web項目的WEB-INF/lib路徑下,併發布到類路徑下。在 WebRoot目錄下編寫一個簡單的登錄頁面 form1. jsp,如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/form1.action" method="post">
        Username: <input type="text" name="username"> <br/>
        Password: <input type="text" name="password"> <br/>
        Address: <input type="text" name="address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

(2)打開web.xml,在web.xml中進行如下配置。

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  

    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  <!--   <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list> -->


</web-app>

(3) 在src目錄下創建struts.xml文件,配置如下示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
    
<struts>
    <constant name="struts.i18n.encoding" value="UTF-8"></constant>

    <package name="demo1" extends="struts-default" namespace="/">
        <action name="form1" class="com.Kevin.form.form1Action"></action>

    </package>
    
    
</struts>

(4)在src目錄下創建com.Kevin.form包,在包中創建一個form1Action

package com.Kevin.form;
/**
 * 使用ActionContext獲取
 */
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class form1Action extends ActionSupport{
    

    public String execute() throws Exception {
        //第一種方式:使用ActionContext類獲取
        //1.獲取ActionContext對象
        ActionContext context=ActionContext.getContext();
        //2.調用方法得到表單數據
        //key是表單輸入項name屬性值,value是輸入值
        Map<String,Object> map=context.getParameters();
        Set<String> keys=map.keySet();
        for(String key:keys){
            //根據key得到value
            //數組形式:因爲輸入項裏可能包含複選框情況
            Object[] obj=(Object[])map.get(key);
            System.out.println(Arrays.toString(obj));
        }        
        return NONE;
    }

}

1.2 通過ServletActionContext訪問(常用方式)

  爲了直接訪問 Servlet API, Struts2框架還提供了 ServletActionContext類,該類包含了幾個常用的靜態方法,具體如下:   ●  static HttpServletRequest  getRequest ( ) :獲取Web應用的 HttpServletRequest 對象。   ●  static HttpServletResponse  getResponse ( ) :獲取Web應用的 HttpServletResponse 對象。   ●  static ServletContext getServletContext ( ) :獲取web應用的 ServletContext 對象。   ●  static PageContext getPageContext ( ) :獲取web應用的 PageContext對象。   接下來,講解如何通過 ServletActionContext訪問 Servlet API。

(1)在src目錄下的com.Kevin.form包中創建一個form2Action,代碼如下:

package com.Kevin.form;
/**
 * 使用ServletActionContext類獲取
 */
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class form2Action extends ActionSupport{
    

    public String execute() throws Exception {
        //第二種方式:使用ServletActionContext類獲取
        //1.使用ServletActionContext類獲取request對象
        HttpServletRequest request=ServletActionContext.getRequest();        
        //2.調用方法得到表單數據
        String name=request.getParameter("username");
        String pwd=request.getParameter("password");
        String address=request.getParameter("address");

        System.out.println(name+" "+pwd+" "+address);
        
        return NONE;
    }

}

(2)在struts.xml文件中進行如下配置:

        <action name="form2" class="com.Kevin.form.form2Action"></action>

(3)更改form1.jsp中配置:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/form2.action" method="post">
        Username: <input type="text" name="username"> <br/>
        Password: <input type="text" name="password"> <br/>
        Address: <input type="text" name="address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

1.3通過特定接口訪問通過(一般不使用)

 Struts2框架提供了ActionContext類來訪問Servlet API,雖然這種方法可以訪問Servlet API,但是無法直接獲得 Servlet API 實例。爲了在 Action中直接訪問 Servlet API,Struts2還提供了一系列接口,具體如下:

●  ServletRequestAware:實現該接口的 Action可以直接訪問Web應用的 HttpServletRequest實例。   ●  ServletRresponseAware:實現該接口的 Action可以直接訪問Web應用的 HttpServletRresponse實例。   ●  SessionAware:實現該接口的 Action可以直接訪問Web應用的 HttpSession實例。   ●  ServletContextAware:實現該接口的 Action可以直接訪問Web應用的ServletContext 實例。   下面以 ServletRequestAware爲例,講解如何在 Action中訪問 HttpServletRequest 實例:

(1)在src目錄下的com.Kevin.form包中創建一個form3Action,代碼如下:

package com.Kevin.form;
/**
 * 使用接口注入
 */
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.ServletRequestAware;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class form3Action extends ActionSupport implements ServletRequestAware{
    private HttpServletRequest request;
    public void setServletRequest(HttpServletRequest request) {
        this.request=request;        
    }

    public String execute() throws Exception {
        String name=request.getParameter("username");
        String pwd=request.getParameter("pasword");
        String address=request.getParameter("address");
        return NONE;
    }

}

(2)在struts.xml文件中進行如下配置:

<action name="form3" class="com.Kevin.form.form3Action"></action>

(3)更改form1.jsp中配置

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/form3.action" method="post">
        Username: <input type="text" name="username"> <br/>
        Password: <input type="text" name="password"> <br/>
        Address: <input type="text" name="address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

二、結果頁面配置

  在 sturts.xml文件中,Result的配置非常簡單,使用< result >元素來配置 Result 邏輯視圖與物理視圖之間的映射,< result>元素可以有name和type屬性,但這兩種屬性都不是必選的。   ●  name屬性:指定邏輯視圖的名稱,默認值爲 success。   ●  type屬性:指定返回的視圖資源的類型,不同的類型代表不同的結果輸出,默認值是dispatcher。 struts. xml文件中的< result>元素配置代碼如下所示:

    <action name="orders" class="com.Kevin.action.OrdersAction">
       <result name="success" type="dispatcher">/hello.jsp</result>
    </action>

  在上述配置中,使用了< result>元素的name、type屬性。其中,爲 Action配置了name爲 success的 Result映射,該映射的值可以是JSP頁面,也可以是一個 Action的name值用type屬性指定了該Result的結果類型爲 dispatcher,它也是默認的結果類型。

  在結果頁面的配置中, Struts2有兩種配置的方式,一種稱爲全局結果頁面,一種稱爲局部結果頁面。全局結果是指在這個包下的所有返回相同字符串的值,都可以向這個頁面來進行跳轉。局部結果是指在某個 Action中返回的字符串的值,會向這個頁面跳轉。

2.1 全局結果頁面

 全局結果頁面是指在同一個包下面配置的action返回相同的字符串的值,都可以跳轉到該頁面。需要通過<global-results>進行配置。

        <!-- 全局結果頁面配置 -->
    <global-results>
      <result name="success">/hello.jsp</result>
    </global-results> 

2.2 局部結果頁面

 局部結果頁面是指在某個action中根據該字符串的值進行頁面的跳轉。只對這個action有效。

    <action name="book" class="com.Kevin.action.BookAction">
       <!-- action訪問名稱 -->
       <result name="success" type="redirectAction">orders</result>
    </action>

  在 Struts2中,當框架調用 Action對請求進行處理後,就要向用戶呈現一個結果視圖。在 Struts中,預定義了多種 ResultType,其實就是定義了多種展示結果的技術。

  一個結果類型就是實現了 com.opensymphony.xwork2.ActionSupport.Result 接口的類,Struts2把內置的result-type都放在 struts- default 包中,sturts- default包就是配置包的父包,這個包定義在struts2-core-2.324jar包中的根目錄下的 struts-default.xml文件中,可以找到相關的 result-type的定義。   每個< result-type>元素都是一種視圖技術或者跳轉方式的封裝,其中的name屬性指出在< result >元素中如何引用這種視圖技術或者跳轉方式,對應着< result>元素的type屬性。 Struts2中預定義的ResultType如表所示:

  其中紅色的幾個值比較常用,需要重點記憶,其他的瞭解即可。到這我們已經瞭解了 Struts2的結果頁面的配置了,也知道如何接收數據了,但是接收過來的數據,往往需要進行封裝纔會向業務層進行傳遞,那麼作爲一個框架,如果連這點功能都沒有,那就太不像是一個“框架”了。那麼在Struts2中提供了對於數據封裝的幾種方式。接下來我們就來學習一下。

三、Struts2的數據封裝

  在很多的實際開發的場景中:頁面提交請求參數到 Action,在 Action中接收參數並且對請求參數需要進行數據的封裝。封裝到一個 Javabean中,然後將 Javabean傳遞給業務層。那麼這些操作Struts2已經替我們都想好了。 Struts2將數據的封裝分成兩大類,一類被稱爲是屬性驅動,一類被稱爲是模型驅動。我們先來看第一種:屬性驅動。   屬性驅動可以細分成兩種,一種只需要提供屬性的set方法即可。另一種可以通過表達式方式直接封裝到對象中。

3.1 屬性驅動

  在Struts2中,可以直接在Action中定義各種Java基本數據類型的字段,使這些字段與表單數據相對應,並利用這些字段進行數據傳遞。

【屬性驅動方式之一:提供屬性的set方法的方式】 

編寫jsp頁面:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/data1.action" method="post">
        Username: <input type="text" name="username"> <br/>
        Password: <input type="text" name="password"> <br/>
        Address: <input type="text" name="address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

編寫action類:

package com.Kevin.form;
/**
 * 使用屬性封裝獲取表單數據
 */

import com.opensymphony.xwork2.ActionSupport;

public class DataDemo1Action extends ActionSupport{
    private String username;
    private String password;
    private String address;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String execute() throws Exception {
        System.out.println(username+password+address);
        return NONE;
    }
}

  以上這種方式需要通過在 Action中定義屬性,並且提供屬性的set和get方法來完成。但若需要傳入的數據很多的話,那麼 Action的屬性也會變得很多。再加上屬性有對應的 getter/setter方法, Action類的代碼會很龐大,在 Action裏編寫業務的代碼時,會使 Action非常臃腫,不夠簡潔。那麼要怎樣解決這個問題呢?   把屬性和相應的 getter/setter方法從 Action裏提取出來,單獨作爲一個值對象,這個對象就是用來封裝這些數據的,在相應的 Action裏直接使用這個對象,而且可以在多個 Action裏使用。採用這種方式,一般以 Javabean來實現,所封裝的屬性和表單的屬性一一對應,Javabean將成爲數據傳遞的載體。

【屬性驅動方式之一:頁面提供表達式的方式】 

編寫jsp頁面:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/data3.action" method="post">
        Username: <input type="text" name="user.username"> <br/>
        Password: <input type="text" name="user.password"> <br/>
        Address: <input type="text" name="user.address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

編寫action類:

package com.Kevin.form;
/**
 * 使用表達式獲取表單數據
 */

import com.Kevin.entity.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class DataDemo3Action extends ActionSupport{
    //1.聲明實體類
    User user;
    //2.聲明實體類的get和set方法
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    
    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }


    
}

3.2 模型驅動封裝

  在 Struts2中,Action處理請求參數還有另外一種方式,叫做模型驅動( ModelDriven )。通過實現 ModelDriven 接口來接收請求參數,Action類必須實現 ModelDriven 接口,並且要重寫 getModel( ) 方法,這個方法返回的就是 Action所使用的數據模型對象。   模型驅動方式通過 Javabean模型進行數據傳遞。只要是普通的 Javabean,就可以充當模型部分。採用這種方式,Javabean所封裝的屬性與表單的屬性一一對應,Javabean將成爲數據傳遞的載體。

【模型驅動】

編寫jsp頁面:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/data2.action" method="post">
        Username: <input type="text" name="username"> <br/>
        Password: <input type="text" name="password"> <br/>
        Address: <input type="text" name="address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

編寫Action:

package com.Kevin.form;
/**
 * 使用模型驅動獲取表單數據
 */
import com.Kevin.entity.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class DataDemo2Action extends ActionSupport implements ModelDriven<User>{
    //創建對象
    //前提:表單屬性值name和實體類中屬性名稱一致
    User user=new User();
    public User getModel() {
        //返回創建user對象
        return user;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }
   
}

3.3 表達式封裝和模型封裝比較

●  相同點:都可以將數據封裝到實體對象裏面

●  不同點:   ①使用模型驅動只能把數據封裝到一個實體類裏(在一個action中不能使用模型封裝把數據封裝到不同的實體類裏)

       ②使用表達式封裝可以把數據封裝到不同的實體類對象裏。

下面使用一個示例來展示用表達式封裝將獲取到的表單數據封裝到不同的實體類中:

首先在src目錄下創建com.Kevin.entity包,在包中創建兩個實體類:

User實體類:

package com.Kevin.entity;

public class User {
    private String username;
    private String password;
    private String address;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [username=" + username + ", password=" + password + ", address=" + address + "]";
    }
    

}

Book實體類:

package com.Kevin.entity;

public class Book {
    private String bname;

    public String getBname() {
        return bname;
    }

    public void setBname(String bname) {
        this.bname = bname;
    }    
}

編寫jsp頁面:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<html>
  <head> 
    <title>My JSP 'form1.jsp' starting page</title>
  </head>
  
  <body>
    <form action="${pageContext.request.contextPath }/data4.action" method="post">
        Username: <input type="text" name="user.username"> <br/>
        Password: <input type="text" name="user.password"> <br/>
        Address: <input type="text" name="user.address"> <br/>
        BookName: <input type="text" name="book.bname"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

編寫Action:

package com.Kevin.form;
/**
 * 使用表達式獲取表單數據,並封裝到不同的實體類中
 * 
 */

import com.Kevin.entity.Book;
import com.Kevin.entity.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class DataDemo4Action extends ActionSupport{
    //1.聲明實體類
    User user;
    Book book;
    
    //2.聲明實體類的get和set方法
    
    public User getUser() {
        return user;
    }
    public Book getBook() {
        return book;
    }
    public void setBook(Book book) {
        this.book = book;
    }
    public void setUser(User user) {
        this.user = user;
    }
    
    @Override
    public String execute() throws Exception {
        System.out.println(user);
        System.out.println(book.getBname());    
        return NONE;
    }
}

配置struts.xml:

        <action name="data4" class="com.Kevin.form.DataDemo4Action"></action>

到這我們已經能夠將數據封裝到一個Java對象中了,大部分我們會優先使用模型驅動的方式,因爲 Struts2內部有很多結果是圍繞模型驅動設計的。但如果頁面向多個對象中封裝,那麼就需要使用屬性驅動的方式二了。這些都是像某個對象中封裝數據,那麼如果 Action中需要一個對象的集合呢?又應該如何進行數據的封裝呢?那麼接下來我們來了解一下 Struts2中複雜類型數據的封裝。

四、Struts2中封裝集合類型的數據

  在實際的開發中,有些時候我們需要批量插入用戶或者批量插入其他的對象,在 Action中需要接受到這多個 Action中封裝的對象,然後傳遞給業務層。那麼這個時候就需要將表單的數據封裝到集合中,一般我們使用的集合無非是List或者是Map集合。下面就以這兩種集合進行數據的封裝的示例演示。

4.1 封裝到List集合中

編寫jsp頁面:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>My JSP 'list1.jsp' starting page</title>
  </head> 
  <body>
        <form action="${pageContext.request.contextPath }/list.action" method="post">
        <!-- list[0]表示list集合中的第一個user對象 -->
        Username: <input type="text" name="list[0].username"> <br/>
        Password: <input type="text" name="list[0].password"> <br/>
        Address: <input type="text" name="list[0].address"> <br/>
        
        Username: <input type="text" name="list[1].username"> <br/>
        Password: <input type="text" name="list[1].password"> <br/>
        Address: <input type="text" name="list[1].address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

編寫Action:

package com.Kevin.form;

import java.util.List;

import com.Kevin.entity.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 封裝數據到List集合
 * @author Kevin
 *
 */

public class ListAction extends ActionSupport{
    
    //1.聲明List變量
    private List<User> list;
    //2.生成get、set方法
    public List<User> getList() {
        return list;
    }
    public void setList(List<User> list) {
        this.list = list;
    }
    
    @Override
    public String execute() throws Exception {
        System.out.println(list);
        return NONE;
    }

}

4.2 封裝到Map集合

編寫jsp頁面:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>My JSP 'list1.jsp' starting page</title>
  </head> 
  <body>
        <form action="${pageContext.request.contextPath }/map.action" method="post">
        <!-- 設置key的值['key值']
            設置value值
         -->
        Username: <input type="text" name="map['one'].username"> <br/>
        Password: <input type="text" name="map['one'].password"> <br/>
        Address: <input type="text" name="map['one'].address"> <br/>
        
        Username: <input type="text" name="map['two'].username"> <br/>
        Password: <input type="text" name="map['two'].password"> <br/>
        Address: <input type="text" name="map['two'].address"> <br/>
        <input type="submit" value="Submit"> <br/>
    </form>
  </body>
</html>

編寫Action:

package com.Kevin.form;

import java.util.Map;

import com.Kevin.entity.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 封裝數據到Map集合
 * @author Kevin
 *
 */

public class MapAction extends ActionSupport{
    //1.聲明map集合
    private Map<String, User> map;
    //2.生成get和set方法

    public Map<String, User> getMap() {
        return map;
    }

    public void setMap(Map<String, User> map) {
        this.map = map;
    }
    
    @Override
    public String execute() throws Exception {
        System.out.println(map);
        return NONE;
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章