S2/X1 and its taglib is oriented towards OGNL, which is using a value stack
for all action properties. These values are not direct available for the
expression language of JSP2/JSTL1.1.
S2/X1和他们的标签库是面向OGNL的,它对所有Action属性用Value栈的。这些值不能直接在JSP2和JSTL1.1种获取。
However, it's easy to populate the request attribute set, with all gettable properties of an action object. You need to provide an interceptor that does the job, by register a PreResultListener which is invoked after the return of Action.execute() but before the rendering of the result .
但是,在一个Action对象中所有可get的属性,是很容易更新请求属性集的。你需要提供一个interceptor来作这件工作,在调用Action.execute之后,渲染结果之前,注册一个PreResultListener。
The interceptor below is using Jakarta BeanUtils. It first extracts all getters
of the current action, invokes them one at the time and stores the values into a map.
下面的interceptor用了Jakarta BeanUtils. 它首先获取所有当前action的getters,调用他们,并且把值保存到map中。
Then it iterates over the map and populates the request attribute set.
The double iteration is not needed, it's just there for clarity.
然后遍历map,组装请求属性集,双重遍历是不需要的,这儿仅仅未了清晰。
class ActionPropertyExportInterceptor
package com.whatever.interceptors;
import org,apache.struts2.StrutsStatics;
import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.AroundInterceptor;
import com.opensymphony.xwork.interceptor.PreResultListener;
import org.apache.commons.beanutils.PropertyUtils;
import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.util.*;
/**
* Populates HTTP Request Attributes with all gettable properties of the current action.
*/
public class ActionPropertyExportInterceptor extends AroundInterceptor {
protected void before(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener( new PropertyExporter() );
}
protected void after(ActionInvocation dispatcher, String result) throws Exception { }
public static class PropertyExporter implements PreResultListener {
private static final List ignore = Arrays.asList(new String[] {"class", "texts"});
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map props = extractGetterPropertyValues( invocation.getAction() );
HttpServletRequest request = getRequest(invocation);
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
Map.Entry e = (Map.Entry) it.next();
request.setAttribute((String) e.getKey(), e.getValue());
}
}
public Map extractGetterPropertyValues(Object bean) {
PropertyDescriptor[] descr = PropertyUtils.getPropertyDescriptors(bean);
Map props = new HashMap();
for (int i = 0; i < descr.length; i++) {
PropertyDescriptor d = descr[i];
if (d.getReadMethod() == null) continue;
if (ignore.contains(d.getName())) continue;
try {
props.put(d.getName(), PropertyUtils.getProperty(bean, d.getName()));
} catch (Exception e) { }
}
return props;
}
public HttpServletRequest getRequest(ActionInvocation invocation) {
return (HttpServletRequest) invocation.getInvocationContext().get(WebWorkStatics.HTTP_REQUEST);
}
}
}
Don't forget to declare the interceptor in your struts.xml file and insert it
into your interceptor stack.
别忘了在struts.xml里声明interceptor,把它插入你interceptor栈中。
struts.xml snippet
<interceptor name="export" class="com.whatever.interceptors.ActionPropertyExportInterceptor" />
. . .
<interceptor-stack name="standard-interceptors">
<interceptor-ref name="timer" />
<interceptor-ref name="logger" />
<interceptor-ref name="params" />
* <interceptor-ref name="export"/>*
<interceptor-ref name="validateParams"/>
<interceptor-ref name="awarePlugger" />
</interceptor-stack>
Your action need to provide getters for all properties that should be exported into the request attribute set.
你的action中需要未被放入request范围的属性提供get方法
class ViewUser
public class ViewUser extends ActionSupport {
private int id;
private User user;
public String execute() throws Exception {
user = findUser( getId() );
return Action.SUCCESS;
}
public int getId() {return id;}
public void setId(int id) {this.id = id;}
* public User getUser() {return user;}*
private User findUser(int id) {...}
}
The User class might look like this
User类可能如下所示:
class User
import java.util.Date;
public class User {
private int id;
private String firstName, lastName, email;
private String street, zip, city;
private Date date;
public String getFirstName() {return firstName;}
}
Finally, using the samples above you can write your JSP2 page like this.
最后,通过上面的例子,你可以写自己的jsp2,类似下面:
ViewUser.jsp
<%@ taglib prefix="c" uri="http: %>
<%@ taglib prefix="fmt" uri="http: %>
<%@ taglib prefix="fn" uri="http: %>
<html>
<head>
<title>Info about ${user.firstName}</title>
</head>
<body>
<h1>Info about ${user.firstName} ${user.lastName} [OS:ID=${user.id}]</h1>
<table border="1" cellspacing="0" cellpadding="2" width="90%" >
<tr>
<th>Name</th> <td>${user.firstName} ${user.lastName}</td>
</tr>
<tr>
<th>Created</th> <td><fmt:formatDate value="${user.date}" pattern="yyyy-MM-dd HH:mm"/></td>
</tr>
<tr>
<th>Email</th> <td>${user.email}</td>
</tr>
<tr>
<th>Address</th> <td>${user.street} ${user.zip} ${fn:toUpperCase(user.city)}</td>
</tr>
</table>
</body>
</html>
Displaying validation errors with JSTL
<c:if test="${!empty fieldErrors || !empty actionErrors}">
<div class="red">
<ul>
<c:forEach items="${fieldErrors}" var="fieldError">
<c:forEach items="${fieldError.value}" var="error">
<li>${error}</li>
</c:forEach>
</c:forEach>
<c:forEach items="${actionErrors}" var="actionError">
<li>${actionError}</li>
</c:forEach>
</ul>
</div>
</c:if>