现在的位置: 首页 > 综合 > 正文

Struts2 Bean级验证

2014年02月18日 ⁄ 综合 ⁄ 共 8560字 ⁄ 字号 评论关闭

今天解决Struts2 Action 级验证当 属性为null时也验证的问题。
测试发现,Struts2 Action 级验证,当被验证的Action中的属性没有被注入值得时候,即是为null的时候,也会执行验证,输出验证信息,估计是因为有必选验证,ognl判断的时候有容错,防止因为属性本身是null,而出异常  ,返回了null, 比如验证实体类中的某个属性,8大基本类型的封装类型和String就不必说了,ognl取值的时候一定返回null。

关于Action级验证和Bean级验证的介绍 : http://kin111.blog.51cto.com/738881/167181 , 大家可以参考。

官方的示例: http://struts.apache.org/release/2.3.x/docs/using-visitor-field-validator.html

上面两个文章中可以看到,Bean级验证就是在Action验证文件里写个visitor验证器的xml,然后再 被验证的 Bean 类所在的包下再一个Bean的验证Xml (放在其他地方会找不到这个验证的配置,struts2就会跳过,而不会出异常!)

VisitorFieldValidator 的源码,就是visitor验证器的实现类

/*
 * Copyright 2002-2006,2009 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.opensymphony.xwork2.validator.validators;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.ValidatorContext;

import java.util.Collection;


/**
 * <!-- START SNIPPET: javadoc -->
 * The VisitorFieldValidator allows you to forward validation to object
 * properties of your action using the object's own validation files.  This
 * allows you to use the ModelDriven development pattern and manage your
 * validations for your models in one place, where they belong, next to your
 * model classes.  The VisitorFieldValidator can handle either simple Object
 * properties, Collections of Objects, or Arrays.
 * <!-- END SNIPPET: javadoc -->
 * <p/>
 *
 * <!-- START SNIPPET: parameters -->
 * <ul>
 * <li>fieldName - field name if plain-validator syntax is used, not needed if field-validator syntax is used</li>
 * <li>context - the context of which validation should take place. Optional</li>
 * <li>appendPrefix - the prefix to be added to field. Optional </li>
 * </ul>
 * <!-- END SNIPPET: parameters -->
 *
 * <pre>
 * <!-- START SNIPPET: example -->
 *    <validators>
 *        <!-- Plain Validator Syntax -->
 *        <validator type="visitor">
 *            <param name="fieldName">user</param>
 *            <param name="context">myContext</param>
 *            <param name="appendPrefix">true</param>
 *        </validator>
 *
 *        <!-- Field Validator Syntax -->
 *        <field name="user">
 *           <field-validator type="visitor">
 *              <param name="context">myContext</param>
 *              <param name="appendPrefix">true</param>
 *           </field-validator>
 *        </field>
 *    </validators>
 * <!-- END SNIPPET: example -->
 * </pre>
 *
 * <!-- START SNIPPET: explanation -->
 * <p>In the example above, if the acion's getUser() method return User object, XWork
 * will look for User-myContext-validation.xml for the validators. Since appednPrefix is true,
 * every field name will be prefixed with 'user' such that if the actual field name for 'name' is
 * 'user.name' </p>
 * <!-- END SNIPPET: explanation -->
 *
 * @author Jason Carreira
 * @author Rainer Hermanns
 * @version $Date: 2009-12-27 19:18:29 +0100 (Sun, 27 Dec 2009) $ $Id: VisitorFieldValidator.java 894090 2009-12-27 18:18:29Z martinc $
 */
public class VisitorFieldValidator extends FieldValidatorSupport {

    private String context;
    private boolean appendPrefix = true;
    private ActionValidatorManager actionValidatorManager;


    @Inject
    public void setActionValidatorManager(ActionValidatorManager mgr) {
        this.actionValidatorManager = mgr;
    }

    /**
     * Sets whether the field name of this field validator should be prepended to the field name of
     * the visited field to determine the full field name when an error occurs.  The default is
     * true.
     */
    public void setAppendPrefix(boolean appendPrefix) {
        this.appendPrefix = appendPrefix;
    }

    /**
     * Flags whether the field name of this field validator should be prepended to the field name of
     * the visited field to determine the full field name when an error occurs.  The default is
     * true.
     */
    public boolean isAppendPrefix() {
        return appendPrefix;
    }

    public void setContext(String context) {
        this.context = context;
    }

    public String getContext() {
        return context;
    }

    public void validate(Object object) throws ValidationException {
        String fieldName = getFieldName();
        Object value = this.getFieldValue(fieldName, object);
        if (value == null) {
            log.warn("The visited object is null, VisitorValidator will not be able to handle validation properly. Please make sure the visited object is not null for VisitorValidator to function properly");
            return;
        }
        ValueStack stack = ActionContext.getContext().getValueStack();

        stack.push(object);

        String visitorContext = (context == null) ? ActionContext.getContext().getName() : context;

        if (value instanceof Collection) {
            Collection coll = (Collection) value;
            Object[] array = coll.toArray();

            validateArrayElements(array, fieldName, visitorContext);
        } else if (value instanceof Object[]) {
            Object[] array = (Object[]) value;

            validateArrayElements(array, fieldName, visitorContext);
        } else {
            validateObject(fieldName, value, visitorContext);
        }

        stack.pop();
    }

    private void validateArrayElements(Object[] array, String fieldName, String visitorContext) throws ValidationException {
        if (array == null) {
            return;
        }

        for (int i = 0; i < array.length; i++) {
            Object o = array[i];
            if (o != null) {
                validateObject(fieldName + "[" + i + "]", o, visitorContext);
            }
        }
    }

    private void validateObject(String fieldName, Object o, String visitorContext) throws ValidationException {
        ValueStack stack = ActionContext.getContext().getValueStack();
        stack.push(o);

        ValidatorContext validatorContext;

        if (appendPrefix) {
            validatorContext = new AppendingValidatorContext(getValidatorContext(), o, fieldName, getMessage(o));
        } else {
            ValidatorContext parent = getValidatorContext();
            validatorContext = new DelegatingValidatorContext(parent, DelegatingValidatorContext.makeTextProvider(o, parent), parent);
        }

        actionValidatorManager.validate(o, visitorContext, validatorContext);
        stack.pop();
    }


    public static class AppendingValidatorContext extends DelegatingValidatorContext {
        private String field;
        private String message;
        private ValidatorContext parent;

        public AppendingValidatorContext(ValidatorContext parent, Object object, String field, String message) {
            super(parent, makeTextProvider(object, parent), parent);

            this.field = field;
            this.message = message;
            this.parent = parent;
        }

        /**
         * Translates a simple field name into a full field name in Ognl syntax
         *
         * @param fieldName field name in OGNL syntax
         * @return field name in OGNL syntax
         */
        @Override
        public String getFullFieldName(String fieldName) {
            return field + "." + fieldName;
        }

        public String getFullFieldNameFromParent(String fieldName) {
            return parent.getFullFieldName(field + "." + fieldName);
        }

        @Override
        public void addActionError(String anErrorMessage) {
            super.addFieldError(getFullFieldName(field), message + anErrorMessage);
        }

        @Override
        public void addFieldError(String fieldName, String errorMessage) {
            super.addFieldError(getFullFieldName(fieldName), message + errorMessage);
        }
    }
}

可以看到有两个属性context 和 appendPrefix是可以在xml配置的,注意的是我测试后发现他们的作用和参考的文章里说的不太一样
context , 这个应该是用来按指定的context的值来区别不同的Bean验证的,默认是Action的Name(),既可以根据这个来实现对同一个Bean 进行不同的验证配置,这个可以根据具体验证的需求自由定义,对应的Bean验证Xml的名字就应该是 BeanName-context-validation.xml ,如果找不到这个,就会找 BeanName-validation.xml 。
appendPrefix , 默认就是true , 这个是用来指定是否在返回的错误的key是否加action中被验证的那个Bean的变量名做前缀的,而不是控制是否把<message>标签中的信息加到验证信息前。

<message>标签中的信息总会被加到验证信息前的,除非是空信息,加上了也看不到。

下是我的代码:
Bean:

public class Supervisor {
	private String username ;
	private String password ;
	private Role role ;
	public void setEnable(Boolean enable) {
		this.enable = enable;
	}
	
	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;
	}
}

RootAction-validation.xml

<?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="supervisor">
		<field-validator type="visitor">
			<!--默认配置,下面两条可不写
 			<param name="context">userContext</param>
            		<param name="appendPrefix">true</param>
			-->
			<message /><!--这个标签必须写-->
		</field-validator>
	</field>
</validators>

Supervisor-validation.xml

<?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="username">
		<field-validator type="requiredstring">
			<message key="username.blank" />
		</field-validator>
		<field-validator type="regex">
			<param name="regex"><![CDATA[^[^\d][\w]+$]]></param>
			<message key="username.illegal"/>
		</field-validator>
	</field>
	
	<field name="password">
		<field-validator type="requiredstring">
			<message key="password.blank" />
		</field-validator>
		<field-validator type="stringlength">
			<param name="minLength">6</param>
            <param name="maxLength">10</param>
            <message key="password.illegal"/>
		</field-validator>
	</field>
</validators>

当appendPrefix为true ,debug中:

false:

还有一点要注意,就是当bean取值为null时,虽然不会执行验证了,但会出个 WARN ,提示说这个Bean 是null的,如果不想看到,可以把log的级别改一下,反正我是看到有WARN也不舒服的~

抱歉!评论已关闭.