我真正想做的是记录ajax请求的实际内容.例如ajax调用的方法的名称(例如,此调用中的“findAddress”:< 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()); }
What I would really like to do is log what the ajax request is actually for. Such as the name of the method being called by the ajax (eg “findAddress” in this call:
<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事件名称(例如,更改,单击,操作等).
获得相关的行为监听器反过来又是一个故事.这在ActionSource2
组件上很容易(例如< h | p:commandButton action =“#{...}”>),因为MethodExpression
在ActionSource2#getActionExpression()
刚刚可用.但是,这在BehaviorBase
标签处理器上并不容易(例如< ; f | p:ajax listener =“#{...}”>)因为此API没有任何方法,如getBehaviorListeners().只有方法可以添加和删除它们,但不能获取它们的列表.因此,使用名称为JSF实现特定的侦听器访问私有字段时,需要一些讨厌的反射技巧.在Mojarra它是listeners
而在MyFaces它是_behaviorListeners
.幸运的是,两者都可以从List中分配,它是该类型中唯一的字段,所以我们可以检查一下.一旦掌握了BehaviorListener
实例的手,那么你仍然需要做另一个反射技巧来获得该实例的MethodExpression字段.呸.
总而言之,这就是在侦听RESTORE_VIEW的afterPhase的PhaseListener风格中的诡计:
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. } }