Java 通过postValidate进行JSF跨域验证,无需在支持bean中按名称查找组件
我正在构建一个登录表单复合组件。使用它的页面将传递一个验证用户名和密码的事件处理程序。通常(不使用复合组件)当我们通过Java 通过postValidate进行JSF跨域验证,无需在支持bean中按名称查找组件,java,validation,jsf,jsf-2,composite-component,Java,Validation,Jsf,Jsf 2,Composite Component,我正在构建一个登录表单复合组件。使用它的页面将传递一个验证用户名和密码的事件处理程序。通常(不使用复合组件)当我们通过postValidate执行跨字段验证时,事件处理程序必须按名称查找字段的组件。验证器最好不要这样做,因为这些是应该抽象的组件的内部细节 知道如何在不知道复合组件内部细节的情况下,在postValidate处理程序中获取用户名和密码字段的转换值吗 更新: 这一点并不是为了避免按名称查找组件,而是为了能够跨字段验证复合组件的字段,而不需要使用页面和/或bean来了解组件的内部细节。
postValidate
执行跨字段验证时,事件处理程序必须按名称查找字段的组件。验证器最好不要这样做,因为这些是应该抽象的组件的内部细节
知道如何在不知道复合组件内部细节的情况下,在postValidate
处理程序中获取用户名和密码字段的转换值吗
更新:
这一点并不是为了避免按名称查找组件,而是为了能够跨字段验证复合组件的字段,而不需要使用页面和/或bean来了解组件的内部细节。f:event postValidate方法不会给您留下很多选项 我更喜欢的选项是对表单的最后一个组件进行验证,然后使用binding和f:attribute传入其他组件 比如说
<h:inputText id="field1" binding="#{field1}" ... />
<h:inputText id="field2" validator="#{...}">
<f:attribute name="field1" value="#{field1}"/>
</h:inputText>
这是可以做到的。在以下代码中,请特别注意复合组件中的
postValidate
事件和支持组件中的postValidate
方法。注意它如何解析MethodExpression
属性并调用传入的方法
下面是复合组件:
或
支持组件:
@FacesComponent(“com.example.LoginForm”)
公共类LoginFormComponent扩展UIInput实现NamingContainer
{
@凌驾
受保护对象getConvertedValue(FacesContext上下文,对象newSubmittedValue)引发ConverterException
{
UIInput emailAddressComponent=(UIInput)findComponent(电子邮件地址ID);
UIInput passwordComponent=(UIInput)findComponent(密码\u ID);
字符串emailAddress=(字符串)emailAddressComponent.getValue();
字符串密码=(字符串)passwordComponent.getValue();
返回新的LoginFormValue(电子邮件地址、密码);
}
公共无效后验证(组件SystemEvent e){
FacesContext ctx=getFacesContext();
//如果用户名和/或密码字段无效,则不验证凭据。
如果(!ctx.getMessageList(EMAIL_ADDRESS_ID).isEmpty()||!ctx.getMessageList(PASSWORD_ID).isEmpty())
{
返回;
}
LoginFormValue=(LoginFormValue)getConvertedValue(null,null);
MethodExpression checkCredentials=(MethodExpression)getAttributes().get(检查凭据属性名称);
checkCredentials.invoke(ctx.getELContext(),新对象[]{getClientId(),value.getEmailAddress(),value.getPassword()});
}
@凌驾
公共字符串getFamily()
{
返回“javax.faces.NamingContainer”;
}
公共静态最终字符串检查\u凭证\u属性\u NAME=“checkCredentials”;
公共静态最终字符串EMAIL\u ADDRESS\u ID=“form:emailAddress”;
公共静态最终字符串密码\u ID=“form:PASSWORD”;
}
LoginFormValue
类的完整性:
公共类LoginFormValue
{
公共LoginFormValue(字符串电子邮件地址、字符串密码)
{
this.emailAddress=电子邮件地址;
this.password=密码;
}
公共字符串getEmailAddress()
{
返回电子邮件地址;
}
公共字符串getPassword()
{
返回密码;
}
私有字符串电子邮件地址;
私有字符串密码;
}
使用登录表单的页面:
登录
最后,页面的支持bean:
@Named
@RequestScoped
public class LoginBean implements Serializable
{
public String getEmailAddress()
{
return emailAddress;
}
public void setEmailAddress(String emailAddress)
{
this.emailAddress = emailAddress;
}
public boolean isRememberMe()
{
return rememberMe;
}
public void setRememberMe(boolean rememberMe)
{
this.rememberMe = rememberMe;
}
/** Action listener for login-form. Called after validation passes. */
public void submit()
{
User user = userDao.findByEmailAddress(emailAddress);
userRequestBean.login(user.getUserId());
// Remember me
if (!rememberMe)
{
return;
}
// Handle rememberMe here (create a cookie, etc.)
}
/** Called by the backing component's postValidate event handler */
public void checkCredentials(String clientId, String emailAddress, String password)
{
if (!securityEjb.checkCredentials(emailAddress, password))
{
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Incorrect email address/password", null);
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.addMessage(clientId, message);
ctx.renderResponse();
}
}
private String emailAddress = "";
private boolean rememberMe = true;
@Inject
private UserRequestBean userRequestBean;
@EJB
private SecurityEjb securityEjb;
@EJB
private UserDao userDao;
@EJB
private LoginCookieDao loginCookieDao;
}
我曾经用过一个类似的问题,我认为它以一种更干净的方式解决了你的问题
与JSF验证器不同,WarningValidator是在模型更新后的呈现响应之前执行的,应用程序已经被调用,因此您可以简单地访问应用程序以获得验证结果
@Named
public class BarWarningValidator implements WarningValidator{
@Inject
private MyValidationBean validationBean;
@Override
public void process(FacesContext context, UIInput component, ValidationResult validationResult) {
if(!validationBean.isBarValid()) {
validationResult.setFacesMessage(new FacesMessage(FacesMessage.SEVERITY_WARN, "FooBar", "This is a warning."));
}
}
}
并将验证程序添加到目标字段:
<h:outputLabel for="bar" value="Default warning:" />
<h:inputText id="bar">
<jw:warning validator="#{barWarningValidator}" />
<f:ajax event="change" render="..." />
</h:inputText>
<h:message for="bar" />
Hmmm。。。这也打破了抽象。我想知道是否可以在复合组件上定义一个双参数(user/pass)方法属性,并从组件的支持类中的事件调用它。组件ID仍然在postValidate
处理程序方法中硬编码。那么,你是说问题不在于硬编码的ID,而在于它们在哪个类中被引用?postValidate
处理程序方法位于支持组件内部,而不是使用它的页面的支持bean。所以它被很好地藏起来,看不见。我可以打包
<h:outputLabel for="bar" value="Default warning:" />
<h:inputText id="bar">
<jw:warning validator="#{barWarningValidator}" />
<f:ajax event="change" render="..." />
</h:inputText>
<h:message for="bar" />