我已经想出如何在过滤器中记录请求是ajax请求以及来自哪个页面。
我真正想做的是记录ajax请求的实际内容。例如ajax调用的方法的名称(例如&#34; findAddress&#34;在此调用中:<p:ajax process="contactDetails" update="@form" listener="#{aboutYouController.findAddress}" ....
)
我该怎么做?我的应用程序有很多ajax请求,我想记录哪些是被触发的。
public class TrackingFilter implements Filter {
private static Logger LOG = Logger.getLogger(TrackingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
String pageHit = req.getRequestURI().substring(req.getContextPath().length()+1).replace(".xhtml", "");
if(!pageHit.contains("javax.faces.resource")){ // if is a url we want to log
if ("partial/ajax".equals(req.getHeader("Faces-Request"))) {
LOG.trace("ajax on URI: " + req.getRequestURI());
}
答案 0 :(得分:10)
我真正想做的是记录ajax请求的实际内容。例如ajax调用的方法的名称(例如&#34; findAddress&#34;在此调用中:
<p:ajax process="contactDetails" update="@form" listener="#{aboutYouController.findAddress}" ....
)
此信息仅在JSF组件树中提供。 JSF组件树仅在视图构建时间之后可用。视图仅在FacesServlet
提供请求时生成。因此,servlet过滤器在任何servlet之前运行时太早。
在回发的还原视图阶段之后,您最好运行代码。 JSF组件树保证在那一刻可用。您可以使用FacesContext#isPostback()
检查当前请求是否为回发。您可以使用PartialViewContext#isAjaxRequest()
检查当前请求是否为ajax请求。您可以使用预定义的javax.faces.source
请求参数来获取ajax请求的源组件的客户端ID。您可以使用预定义的javax.faces.behavior.event
请求参数来获取ajax事件名称(例如change
,click
,action
等。
获取相关的行为监听器又是一个故事。这在ActionSource2
组件(例如<h|p:commandButton action="#{...}">
)上很容易,因为MethodExpression
只有ActionSource2#getActionExpression()
可用。但是,BehaviorBase
代码处理程序(例如<f|p:ajax listener="#{...}">
)并不容易,因为此API没有getBehaviorListeners()
之类的任何方法。只有方法可以添加和删除它们,但不能获取它们的列表。因此,使用名称为JSF实现特定的侦听器访问private
字段时,需要一些讨厌的反射技巧。在Mojarra,listeners
和MyFaces _behaviorListeners
。幸运的是,两者都可以从List
分配,并且它是该类型的唯一字段,因此我们可以检查它。一旦掌握了BehaviorListener
实例,那么仍然需要做另一个反射技巧来获取该实例的MethodExpression
字段。呸。
总而言之,这里的PhaseListener
afterPhase
RESTORE_VIEW
的{{1}}听起来有点诡计:
public class AjaxActionLoggerPhaseListener implements PhaseListener {
@Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
@Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
@Override
public void afterPhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (!(context.isPostback() && context.getPartialViewContext().isAjaxRequest())) {
return; // Not an ajax postback.
}
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
String sourceClientId = params.get("javax.faces.source");
String behaviorEvent = params.get("javax.faces.behavior.event");
UIComponent source = context.getViewRoot().findComponent(sourceClientId);
List<String> methodExpressions = new ArrayList<>();
if (source instanceof ClientBehaviorHolder && behaviorEvent != null) {
for (ClientBehavior behavior : ((ClientBehaviorHolder) source).getClientBehaviors().get(behaviorEvent)) {
List<BehaviorListener> listeners = getField(BehaviorBase.class, List.class, behavior);
if (listeners != null) {
for (BehaviorListener listener : listeners) {
MethodExpression methodExpression = getField(listener.getClass(), MethodExpression.class, listener);
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
}
}
}
if (source instanceof ActionSource2) {
MethodExpression methodExpression = ((ActionSource2) source).getActionExpression();
if (methodExpression != null) {
methodExpressions.add(methodExpression.getExpressionString());
}
}
System.out.println(methodExpressions); // Do your thing with it.
}
private static <C, F> F getField(Class<? extends C> classType, Class<F> fieldType, C instance) {
try {
for (Field field : classType.getDeclaredFields()) {
if (field.getType().isAssignableFrom(fieldType)) {
field.setAccessible(true);
return (F) field.get(instance);
}
}
} catch (Exception e) {
// Handle?
}
return null;
}
}
要使其运行,请在faces-config.xml
:
<lifecycle>
<phase-listener>com.example.AjaxActionLoggerPhaseListener</phase-listener>
</lifecycle>
以上版本经过测试并与Mojarra和PrimeFaces兼容,理论上也与MyFaces兼容。
更新:如果您正在使用JSF实用程序库OmniFaces,或者从版本2.4开始使用,您可以使用新的Components#getCurrentActionSource()
实用程序方法找出当前的动作源组件和Components#getActionExpressionsAndListeners()
以获取在给定组件上注册的所有动作方法和侦听器的列表。这也适用于常规(非ajax)请求。有了这个,上面的PhaseListener
示例可以减少如下:
public class FacesActionLoggerPhaseListener implements PhaseListener {
@Override
public PhaseId getPhaseId() {
return PhaseId.PROCESS_VALIDATIONS;
}
@Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
@Override
public void afterPhase(PhaseEvent event) {
if (!event.getFacesContext().isPostback())) {
return;
}
UIComponent source = Components.getCurrentActionSource();
List<String> methodExpressions = Components.getActionExpressionsAndListeners(source);
System.out.println(methodExpressions); // Do your thing with it.
}
}