1. 程式人生 > >Struts2學習之2(請求引數的封裝,型別轉換,輸入驗證,自定義攔截器)

Struts2學習之2(請求引數的封裝,型別轉換,輸入驗證,自定義攔截器)

封裝請求引數

動態引數注入(請求引數,使用者輸入的)

方式一(動作類作為模型)

  • 頁面
<form action="${pageContext.request.contextPath}/q1/login.do" method="post">
    <!-- name屬性對應著動作類中的屬性-->
    UserName:<input name="userName" type="text"/><br/>
    Password:<input name="password" type="password"/><br
/>
<input type="submit" value="登入"/> </form>
  • 動作類
package com.jyh.action;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class UserAction extends ActionSupport {

    //編寫與表單裡面名字相同的私有屬性並提供相應的getset方法
    private String userName;
    private String password;
    public
void setUserName(String userName) { this.userName = userName; } public void setPassword(String password) { this.password = password; } public String getUserName() { return userName; } public String getPassword() { return password; } public
String login(){ System.out.println(userName + ":" + password); if("jyh".equals(userName) && "123".equals(password)) return SUCCESS; return ERROR; } }

方式二(動作類和模型分開)

  • 頁面
<form action="${pageContext.request.contextPath}/q2/login.do" method="post">
    <!-- name屬性中person代表著動作類中模型物件的例項 -->
    UserName:<input name="person.userName" type="text"/><br/>
    Password:<input name="person.password" type="password"/><br/>
    <input type="submit" value="登入"/>
</form>
  • 動作類
package com.jyh.action;

import com.jyh.domain.Person;
import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class PersonAction extends ActionSupport {

    private Person person = new Person();

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }
    public String login(){
        System.out.println(person);
        if("jyh".equals(person.getUserName()) && "123".equals(person.getPassword()))
            return SUCCESS;
        return ERROR;
    }
}

方式三(動作類和模型分開,使用ModelDriven模型驅動)

  • 頁面
<form action="${pageContext.request.contextPath}/q3/login.do" method="post">
    UserName:<input name="userName" type="text"/><br/>
    Password:<input name="password" type="password"/><br/>
    <input type="submit" value="登入"/>
</form>
  • 動作類
package com.jyh.action;

import com.jyh.domain.Person;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

@SuppressWarnings("serial")
public class PersonAction2 extends ActionSupport implements ModelDriven<Person>{

    private Person person = new Person();

    public String login(){
        System.out.println(person);
        if("jyh".equals(person.getUserName()) && "123".equals(person.getPassword()))
            return SUCCESS;
        return ERROR;
    }

    @Override
    public Person getModel() {
        return person;
    }
}

模型就是JavaBean模型類,該功能是由modelDriven攔截器完成的,與struts2的值棧有關。

靜態引數注入

就是在struts.xml配置檔案中的action下面配置引數<param name="name">名字</param>
然後就給動作類對應的屬性賦值了。

引數注入是由兩個攔截器來完成。
靜態引數注入:staticParams
動態引數注入:params

自定義型別轉換

編寫型別轉換實現類

package com.jyh.convertors;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;
//1、實現MM/dd/yyyy<------>java.util.Date互相轉換
@SuppressWarnings("all")
public class MyDateConverter extends StrutsTypeConverter {

    private DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
    //String--->其他型別:Date
    /**
     * String[] values:使用者輸入的值.
     * Class toClass:目標型別
     */
    public Object convertFromString(Map context, String[] values, Class toClass) {
        if(toClass!=Date.class){
            throw new RuntimeException("您轉換的不是日期,請選擇別的轉換器");
        }
        if(values==null||values.length==0)
            throw new IllegalArgumentException("沒有資料");
        String sDate = values[0];//使用者輸入的是""
        if(!sDate.trim().equals("")){
            try {
                return df.parse(sDate);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }
    //其他型別:Date---->String
    /**
     * Object o:物件
     */
    public String convertToString(Map context, Object o) {
        if(!(o instanceof Date)){
            throw new RuntimeException("您的資料不是日期,請選擇別的轉換器");
        }
        Date date = (Date)o;
        return df.format(date);
    }

}

宣告定義配置

區域性型別轉換器,為某個類的某個屬性欄位進行驗證

  • 動作和模型分開
    配置檔案放在模型類所在包,檔名為:模型類名-conversion.properties
    這裡寫圖片描述
  • 動作和模型不分開
    配置檔案放在動作類所在包,檔名為:動作類名-conversion.properties

全域性轉換器,為某個型別進行驗證

配置檔案放在構建路徑頂端,檔名為:xwork-conversion.properties,裡面一般是需要驗證的型別對應自定義的轉換器,比如java.util.Date=com.jyh.convertors.MyDateConverter。

轉換失敗之後的提示

  1. 轉換失敗,會自動轉到一個name=input的邏輯檢視,一般指向輸入的那個頁面,目的回顯(建議使用struts2的表單標籤)
  2. 錯誤訊息提示中文版本
    前提:動作類繼承ActionSupport才能使用。
    檔名為:類名.properties
    這裡寫圖片描述

總結就是轉換器配置檔名為:需要驗證的屬性所在類的類名-conversion.properties
中文提示資訊的配置檔名為:被驗證的屬性所在類的類名.properties

輸入驗證

程式設計式驗證

缺點:驗證規則寫到了程式碼中,硬編碼。
優點:驗證時可以控制的更加精細。(用的少)

針對所有方法進行驗證

1.動作類需要實現ActionSupport,覆蓋掉public void validate(){}方法,
2.方法內部:編寫你的驗證規則,不正確的情況呼叫addFieldError新增錯誤資訊

public void validate() {
    if("".equals(student.getUsername())){
        addFieldError("username", "請輸入使用者名稱");
    }
}

使用username這種方式新增錯誤資訊s:fieldError標籤中和普通標籤中都有錯誤提示訊息,使用了,將只會在s:fieldError中顯示錯誤訊息
3.驗證失敗:
檢視:會自動轉向一個name=input的邏輯檢視
錯誤訊息提示:建議使用struts2標籤庫。如果沒有顯示請使用s:fieldError標籤

針對指定的方法進行驗證

方式一

在指定方法前面新增一個註解:@SkipValidation,有該註解的方法就不會進行驗證

方式二

在需要驗證的方法,編寫一個方法,命名為validate+需要驗證的方法名,需要驗證的方法名首字母需要大寫,如:如果一個動作方法名叫做regist,只針對該方法進行驗證,就編寫public void validateRegist(){};
這裡寫圖片描述

宣告式驗證

優點:把驗證規則寫到了配置檔案中。(用得多)
缺點:不是很精細。
錯誤檢視和訊息提示和程式設計式一致。

針對所有方法進行驗證

在動作類所在的包中建立:動作類名-validation.xml配置檔案。
頭部宣告可以在struts包/xwork-core-xxx.jar/xwork-validator-xxx.dtd中找到,該dtd檔案就是驗證配置檔案的約束。

針對指定的方法進行驗證

檔名改為:動作類名-動作名(struts.xml中action的name屬性)-validation.xml

說明:驗證功能是由validation攔截器來負責處理的。回顯錯誤資訊是由workflow攔截器來負責處理的。

struts2中提供的內建宣告式驗證器的使用

Struts2提供的宣告式驗證器在xwork-core-**.jar包的
com.opensymphony.xwork2.validator.validators.default.xml配置檔案中。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator Definition 1.0//EN"
        "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">

<!-- START SNIPPET: validators-default -->
<validators>
    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
    <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
    <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
    <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
    <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
    <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
    <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>
<!--  END SNIPPET: validators-default -->

驗證配置檔案寫法:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator 1.0.3//EN"
        "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
    <!-- field指定要驗證的欄位,name是欄位名。和表單一致 -->
    <field name="username">
        <!-- 必須由3~8位字母組成-->
        <field-validator type="regex"><!-- type為驗證型別,也就是驗證器的名字 -->
            <param name="regexExpression"><![CDATA[[a-zA-Z]{3,8}]]></param><!-- param是指定驗證器的屬性引數 -->
             <message>你的名字必須由3~8位字母組成</message><!-- message是返回的錯誤資訊 -->
        </field-validator>
    </field>
    <!-- 驗證密碼必須3~8位數字組成:換一種 -->
    <!-- 
    <validator type="regex">
        <param name="fieldName">password</param>
        <param name="regexExpression"><![CDATA[\d{3,8}]]></param>
         <message>你的密碼必須由3~8位數字組成</message>
    </validator>
     -->
     <field name="password">
        <field-validator type="requiredstring">
            <message>請輸入密碼</message>
        </field-validator>
        <field-validator type="strongpassword">
            <message>你的密碼必須由大小寫字母和數字組成</message>
        </field-validator>
     </field>
    <!-- 必須選擇性別 -->
    <field name="gender">
        <field-validator type="required">
             <message>請選擇性別</message>
        </field-validator>
    </field>
    <field name="email">
        <field-validator type="email">
             <message>請輸入正確的郵箱</message>
        </field-validator>
    </field>
</validators>

自定義宣告式驗證

驗證密碼的強度:至少一個大寫、小寫、數字組成。
這裡寫圖片描述
1.驗證的實現:編寫一個類,繼承FieldValidatorSupport。

package com.jyh.validators;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;

public class StrongPasswordValidator extends FieldValidatorSupport {
    private boolean trim = true;

    public boolean isTrim() {
        return trim;
    }

    public void setTrim(boolean trim) {
        this.trim = trim;
    }

    // Object object:當前執行的動作類
    public void validate(Object object) throws ValidationException {
        String fieldName = getFieldName();// 當前要驗證的欄位名
        Object value = this.getFieldValue(fieldName, object);// 獲取使用者輸入的值
        if (!(value instanceof String)) {
            addFieldError(fieldName, object);
        } else {
            String s = (String) value;

            if (trim) {
                s = s.trim();
            }

            if (!isPasswordStrong(s)) {
                addFieldError(fieldName, object);
            }
        }
    }

    private static final String GROUP1 = "abcdefghijklmnopqrstuvwxyz";
    private static final String GROUP2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String GROUP3 = "0123456789";

    protected boolean isPasswordStrong(String password) {
        boolean ok1 = false;
        boolean ok2 = false;
        boolean ok3 = false;
        int length = password.length();
        for (int i = 0; i < length; i++) {
            if (ok1 && ok2 && ok3)
                break;
            String character = password.substring(i, i + 1);
            if (GROUP1.contains(character)) {
                ok1 = true;
                continue;
            }
            if (GROUP2.contains(character)) {
                ok2 = true;
                continue;
            }
            if (GROUP3.contains(character)) {
                ok3 = true;
                continue;
            }
        }
        return ok1 && ok2 && ok3;
    }
}

2.驗證的宣告:在構建路徑頂端,建立一個固定名稱為validators.xml的配置檔案,配置檔案參考
xwork-core-xxx.jar包的com.opensymphony.xwork2.validator.validators.default.xml。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator Definition 1.0//EN"
        "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<validators>
    <!-- name為驗證器的名字,class為驗證器的實現類 -->
    <validator name="strongpassword" class="com.jyh.validators.StrongPasswordValidator"/>
</validators>

3.驗證器的使用

<field name="password">
        <field-validator type="requiredstring">
            <message>請輸入密碼</message>
        </field-validator>
        <!-- type為驗證器的name屬性 -->
        <field-validator type="strongpassword">
            <message>你的密碼必須由大小寫字母和數字組成</message>
        </field-validator>
     </field>

自定義攔截器

編寫一個類,繼承AbstractInterceptor抽象類或者MethodFilterInterceptor抽象類

package com.jyh.interceptors;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

@SuppressWarnings("serial")
//繼承MethodFilterInterceptor可以有選擇的進行攔截與不攔截
public class Demo1Interceptor extends MethodFilterInterceptor/*AbstractInterceptor*/{

    public String doIntercept(ActionInvocation invocation) throws Exception {
        System.out.println("攔截前");
        String valueString = invocation.invoke();//放行
        System.out.println("攔截後");
        return valueString;
    }

}

宣告定義與使用自定義攔截器

    <!-- 定義一個包,用來進行攔截器的定義初始化 -->
    <package name="myDefaultPackage" extends="struts-default">
        <!-- 宣告定義攔截器 -->
        <interceptors>
            <!-- 攔截器名和其實現類 -->
            <interceptor name="demo1Interceptor" class="com.jyh.interceptors.Demo1Interceptor"></interceptor>
            <!-- 定義一個攔截器棧 -->
            <interceptor-stack name="mydefault">
                <!-- 包含預設的defaultStack棧 -->
                <interceptor-ref name="defaultStack"></interceptor-ref>
                <!-- 包含自己定義的攔截器 -->
                <interceptor-ref name="demo1Interceptor"></interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <!-- 覆蓋預設的攔截器棧 -->
        <default-interceptor-ref name="mydefault"></default-interceptor-ref>
    </package>

    <!-- 繼承剛剛定義的包 -->
    <package name="demo" extends="myDefaultPackage" namespace="/demo">
        <action name="demo1" class="com.jyh.action.Demo1Action" method="add">
            <!-- 使用攔截器,將會 覆蓋所有預設攔截器 -->
            <interceptor-ref name="demo1Interceptor">
                <!-- 新增引數excludeMethods代表不攔截,後面為不攔截的方法名(當前訪問的方法名)
                includeMethods代表攔截 -->
                <param name="excludeMethods">add</param>
            </interceptor-ref>
            <result name="success">/demo1.jsp</result>
        </action>

        <action name="demo2" class="com.jyh.action.Demo1Action" method="del">
            <!-- 使用攔截器棧,將會 覆蓋所有預設攔截器,但是因為繼承了預設的攔截器棧,所以儲存了預設的攔截器 -->
            <interceptor-ref name="mydefault"></interceptor-ref>
            <result name="success">/demo1.jsp</result>
        </action>
    </package>