ajax - 如何记录 JSF ajax 请求的方法表达式

标签 ajax jsf logging methodexpression

我已经弄清楚如何在过滤器中记录请求是 ajax 请求的情况以及它来自哪个页面。

我真正想做的是记录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 组件树仅在 View 构建时间之后可用。仅当 FacesServlet 满足请求时才会构建 View 。因此,servlet 过滤器太早了,因为它在任何 servlet 之前运行。

您最好在回发的恢复 View 阶段之后运行代码。 JSF 组件树保证在那一刻可用。您可以使用 FacesContext#isPostback() 检查当前请求是否是回发。您可以使用 PartialViewContext#isAjaxRequest() 检查当前请求是否是ajax请求。您可以使用预定义的 javax.faces.source request参数获取ajax请求源组件的客户端ID。您可以使用预定义的 javax.faces.behavior.event请求参数获取ajax事件名称(例如 changeclickaction 等)。

获取相关的行为监听器又是另外一回事了。这很容易 ActionSource2 组件(例如 <h|p:commandButton action="#{...}"> )作为 MethodExpression 只能通过 ActionSource2#getActionExpression() 获得。然而,这对 BehaviorBase 来说并不容易。标记处理程序(例如 <f|p:ajax listener="#{...}"> ),因为此 API 没有任何类似 getBehaviorListeners() 的方法。只有添加和删除它们的方法,但没有获取它们的列表的方法。因此,需要一些令人讨厌的反射技巧来访问 private字段包含名称特定于 JSF 实现的监听器。在莫哈拉,它是 listeners 在 MyFaces 中,它是 _behaviorListeners 。幸运的是,两者都可以从 List 分配。这是该类型的唯一字段,因此我们可以检查它。一旦拥有 BehaviorListener 之手例如,那么您仍然需要执行另一个反射技巧来获取 MethodExpression该实例的字段。恶心。

总而言之,这就是 PhaseListener 风格的诡计。正在收听afterPhaseRESTORE_VIEW :

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.
    }

}

关于ajax - 如何记录 JSF ajax 请求的方法表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31853188/

相关文章:

java - eclipse中突然出现ClassNotFoundException

java - log4j级别跟踪和调试没有显示任何日志

记录到 Blazor WASM 中的文件

javascript - 使用 XHR 的 Chrome 推送通知(使用 JavaScript,不使用 PHP)

jsf - 如果表未渲染,reRender 会如何表现

javascript - onchange javascript 在 jquerymobile 中提交换行符

css - 从 JSF 托管 bean 生成 CSS

java - 如何在 Play 框架的文件中记录数据?

javascript - Jquery 自动完成功能不起作用 - 没有搜索结果

java - 如何将 bytearray 转换为 img 标签?