2

为了提供正确的浏览器缓存,我想去掉conversationContextApache MyFaces Orchestra 添加到每个请求的参数,用于对 css 文件的请求。

正如Bozho建议的那样,我已经实现了一个过滤器来设置 Orchestra 正在寻找的属性。

public class ResourceFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse theResponse, FilterChain theChain) throws IOException, ServletException {
    if(shouldNotAppendConversation(request)) {
        request.setAttribute(RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED, Boolean.TRUE);
    }

    theChain.doFilter(request, theResponse);
}

private boolean shouldNotAppendConversation(ServletRequest theRequest) {
    HttpServletRequest aRequest = (HttpServletRequest) theRequest;
    String aPath = aRequest.getRequestURI();
    if(aPath.endsWith(".css.jsf")) {
        return true;
    }

    return false;
}

@Override
public void init(FilterConfig theFilterConfig) throws ServletException {
}

@Override
public void destroy() {
}
}

这不起作用,参数仍然附加到每个请求。在调试时,我发现过滤器首先受到对 jsf 站点的请求的影响。当然,我想conversation context在该请求中包含 ,因此过滤器将请求直接转发到链中的下一个过滤器。命中过滤器的下一个请求(通常是对 css 文件的请求)已经conversation context包含在请求中。

奇怪的是,如果我将过滤器修改为始终设置属性,则所有请求都不会具有该conversation context属性。但这意味着,conversation contextjsf 站点的请求中也不包含(但应该)。

我注意到 jsf 站点生成的 html 中指向 css 文件的链接也包含该conversation context属性或不包含该属性,具体取决于过滤器实现。我猜出于这个原因,第二个请求已经包含了conversation context参数?

我不明白为什么 Orchestra 将conversation context参数附加到每个请求,而不仅仅是未设置属性的请求。

如何实现过滤器以正常工作?

4

2 回答 2

3

在对您的页面的请求之后点击您的过滤器的下一个请求(例如,对于 CSS 文件)已经conversationContext包含该参数,因为这是该资源的 url 在上一个请求中的页面已呈现的方式。

所以conversationContext应该在渲染时进行控制。以下解决方案适用于 JSF 2(我使用的是 Mojarra 2.1.11、myfaces-orchestra-core20 1.5、RichFaces 4.1.0.Final)。一个特殊的 servlet 过滤器什么都不做,只是HttpServletResponse用我们自己的包装器进行包装:

public class RfOrchestraParamControlFilter implements Filter {
   ...
   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      response = new RfOrchestraParamControlResponseWrapper((HttpServletResponse)response);
      chain.doFilter(request, response);
   }
   ...
}

响应包装器测试要编码的 url 是否为richfaces 资源ConversationRequestParameterProvider,并在编码时在当前线程中打开 Orchestra 的分离模式:

package ...
import javax.faces.application.ResourceHandler;
import org.richfaces.resource.ResourceHandlerImpl;
import org.apache.myfaces.orchestra.conversation.ConversationRequestParameterProvider;

public class RfOrchestraParamControlResponseWrapper extends HttpServletResponseWrapper {

   public RfOrchestraParamControlResponseWrapper(HttpServletResponse httpServletResponse) {
      super(httpServletResponse);
   }

   @Override
   public String encodeURL(String url) {
      if (url.contains(ResourceHandler.RESOURCE_IDENTIFIER) || url.contains(ResourceHandlerImpl.RICHFACES_RESOURCE_IDENTIFIER)) {
         boolean current = ConversationRequestParameterProvider.isInSeparationMode();
         /* Disable conversationContext parameter in current thread for the time of rendering link to a resource */
         ConversationRequestParameterProvider.setInSeparationMode(true);

         String result = super.encodeURL(url);

         /* Restore */
         ConversationRequestParameterProvider.setInSeparationMode(current);
         return result;
      }
      else return super.encodeURL(url);
   }

}

(我不得不使用String.contains()而不是String.startsWith()在测试作为资源的 url 作为上下文路径和 servlet 路径恰好在传递的 url 之前。)

然而,此时此刻这也无济于事。原因是 Orchestra 使用它自己的响应包装,它发生在它的 中RequestParameterFacesContextFactory,并且这种包装发生我们的过滤器被命中之后。通过这种方式,Orchestra 的包装器在我们的包装器外部,这导致我们的包装器接收url得太晚,此时 url 已经被拦截和conversationContext附加。

为了避免这种情况,我们有一种方法可以通过替换拦截器的效果来使我们的响应包装器在管弦乐队的响应包装器之外,而RequestParameterFacesContextFactory拦截器RequestParameterServletFilter实际上使用它来完成相同的工作。不幸的是,在我们可能不使用的地方使用另一个过滤器并不是很精致,但到目前为止我还没有看到另一种方法。

因此,在管弦乐队的过滤器之后web.xml放置您的过滤器:

<filter>
   <filter-name>requestParameterFilter</filter-name>
   <filter-class>org.apache.myfaces.orchestra.requestParameterProvider.RequestParameterServletFilter</filter-class>
</filter>
<filter>
   <filter-name>myOrchestraFilter</filter-name>
   <filter-class>mypkg.RfOrchestraParamControlFilter</filter-class>
</filter>

<filter-mapping>
   <filter-name>requestParameterFilter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
<filter-mapping>
   <filter-name>myOrchestraFilter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
于 2012-11-19T23:36:47.053 回答
0

无法使以前的解决方案起作用。我是这样做的:

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.stream.Collectors;

import javax.faces.application.ResourceHandler;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;

/**
 * @author Felipe Riccetto
 */
public class RemoveConversationParamFilter implements Filter {

    public static String removeQueryParameter(final String url,
            final String parameterName) throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(url);
        List<NameValuePair> queryParameters = uriBuilder.getQueryParams()
                .stream().filter(p -> !p.getName().equals(parameterName))
                .collect(Collectors.toList());
        if (queryParameters.isEmpty()) {
            uriBuilder.removeQuery();
        } else {
            uriBuilder.setParameters(queryParameters);
        }
        return uriBuilder.build().toString();
    }

    @Override
    public void destroy() {
        // nothing
    }

    @Override
    public void doFilter(final ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {

        HttpServletResponse resp2 = new HttpServletResponseWrapper(
                (HttpServletResponse) resp) {
            @Override
            public String encodeURL(String url) {

                String s = super.encodeURL(url);

                try {
                    String urlPath = new URL(
                            ((url.toLowerCase().startsWith("http://")
                                    || url.toLowerCase().startsWith("https://"))
                                            ? ""
                                            : "http://fake")
                                    + url).getPath().toString().toLowerCase();
                    if (urlPath.endsWith(".js") || urlPath.endsWith(".css")
                            || urlPath.endsWith(".png")
                            || urlPath.endsWith(".jpeg")
                            || urlPath.endsWith(".jpg")
                            || urlPath.endsWith(".gif") || urlPath.contains(
                                    ResourceHandler.RESOURCE_IDENTIFIER)) {
                        s = removeQueryParameter(s, "conversationContext");
                    }

                } catch (MalformedURLException | URISyntaxException e) {
                    // ignore
                }
                return s;
            }
        };

        chain.doFilter(req, resp2);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // nothing
    }
}

网页.xml:

<filter>
    <filter-name>RemoveConversationParamFilter</filter-name>
    <filter-class>RemoveConversationParamFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>RemoveConversationParamFilter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

最后一个映射必须是 web.xml 中的最后一个映射

于 2019-03-27T01:38:08.397 回答