java 防止在页面加载时向资源添加后缀

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14963756/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 18:01:44  来源:igfitidea点击:

Prevent suffix from being added to resources when page loads

javajsfjsf-2akamai

提问by blo0p3r

I have a JSF2 application running and working no problem. The issue I am having with JSF is with the resource bundle. All resources have the .xhtmlsuffix appended to it. So main.cssbecomes main.css.xhtmlwhen loaded in the browser. I would like to have it so the .xhtmlisn't apended to the resources (don't mind about the pages themselves).

我有一个 JSF2 应用程序正在运行并且没有问题。我在 JSF 中遇到的问题是资源包。所有资源都.xhtml附加了后缀。所以在浏览器中加载时main.css就变成main.css.xhtml了。我想要它,所以.xhtml它不会附加到资源中(不要介意页面本身)。

Is there a way where we can NOThave .xhtmlappended to resources?

是否有一个地方,我们可以一个方式具有.xhtml附加的资源呢?

I would ideally not have to change the internal workings of the site. I have listed ideas below, but I have to say I don't really like these. Hoping for a solution somewhere?

理想情况下,我不必更改网站的内部运作。我在下面列出了一些想法,但我不得不说我真的不喜欢这些。希望在某处找到解决方案?

I am using Majorra v.2.1.17 on Glassfish 3.1.2.2.

我在 Glassfish 3.1.2.2 上使用 Majorra v.2.1.17。

Current Faces Servlet loading as in web.xml (updated)

在 web.xml 中加载 Current Faces Servlet(更新)

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>

Why this questions is different from others

为什么这个问题与其他问题不同

Reasoning

推理

Sure you might be asking me why I need this. Well, we are moving our application to be served by the Akamai CDN.

当然你可能会问我为什么需要这个。好吧,我们正在将我们的应用程序转移到 Akamai CDN 提供服务。

The issue we are having with the integration of the site is that we are trying to cache static content on the edge servers. This is done by matching file extensions (ie: .js, .doc, .png, css, etc). We cannot match xhtmlbecause this would be caching all pages as well as static content. Which by that would cause problems with sessions and such.

我们在站点集成中遇到的问题是我们正在尝试在边缘服务器上缓存静态内容。这是通过匹配文件扩展名(即:.js、.doc、.png、css 等)来完成的。我们无法匹配,xhtml因为这会缓存所有页面以及静态内容。这会导致会话等问题。

Attempted Solution

尝试的解决方案

In line with the answer by BalusC, I have implemented the resource handler as suggested. I will not rewrite code here, since it is in answer below.

根据 BalusC 的回答,我已经按照建议实现了资源处理程序。我不会在这里重写代码,因为它在下面的答案中。

However, I am getting an error when loading composite components. I am getting an error as such :

但是,加载复合组件时出现错误。我收到这样的错误:

WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
java.lang.NullPointerException
    at com.sun.faces.application.ApplicationImpl.createComponent(ApplicationImpl.java:975)
    at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.createComponent(CompositeComponentTagHandler.java:162)
    at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.createComponent(ComponentTagHandlerDelegateImpl.java:494)
    at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:169)
...

Composite component is loaded correctly because if I "unregister" the new ResourceHandlerwe just created it will load. The stack trace leads me to believe that it is trying to find this component in a java class, instead of finding it in the resources. According to grepcodethis would be at this last line (975) where the error happens :

复合组件已正确加载,因为如果我“取消注册”ResourceHandler我们刚刚创建的新组件,它将加载。堆栈跟踪让我相信它正在尝试在 java 类中找到这个组件,而不是在资源中找到它。根据grepcode这将在最后一行(975)发生错误:

String packageName = componentResource.getLibraryName();
String className = componentResource.getResourceName();
className = packageName + '.' + className.substring(0, className.lastIndexOf('.'));

Meaning that the resourceName, aka classNameis nullsince the error I am getting is java.lang.NullPointerException. I can't seem to figure out how/where the ResourceHandleris called vis-a-vis a composite component. Any help figuring out this last issue?

这意味着resourceName,又名classNamenull因为我得到的错误是java.lang.NullPointerException. 我似乎无法弄清楚ResourceHandler相对于复合组件如何/在哪里调用它。对解决最后一个问题有帮助吗?

回答by BalusC

This is doable with a custom ResourceHandlerwhich returns in createResource()a Resourcewhich in turn returns an "unmapped" URL on Resource#getRequestPath(). You only need to add the default JSF resource prefix /javax.faces.resource/*to the <url-pattern>list of the FacesServletmapping in order to get it to be triggered anyway.

这是可行的具有自定义ResourceHandler它返回createResource()一个Resource这反过来又返回上一个“未映射”的URL Resource#getRequestPath()。您只需要将默认的 JSF 资源前缀添加/javax.faces.resource/*到映射<url-pattern>列表中FacesServlet,以便无论如何都能触发它。

Further, you need to override isResourceRequest()to check if the URL starts with the JSF resource prefix and also the handleResourceRequest()to locate and stream the proper resource.

此外,您需要覆盖isResourceRequest()以检查 URL 是否以 JSF 资源前缀开头,以及handleResourceRequest()定位和传输正确的资源。

All with all, this should do:

总之,这应该做:

public class UnmappedResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public UnmappedResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public Resource createResource(final String resourceName, final String libraryName) {
        final Resource resource = super.createResource(resourceName, libraryName);

        if (resource == null) {
            return null;
        }

        return new ResourceWrapper() {

            @Override
            public String getRequestPath() {
                ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
                String mapping = externalContext.getRequestServletPath();

                if (externalContext.getRequestPathInfo() == null) {
                    mapping = mapping.substring(mapping.lastIndexOf('.'));
                }

                String path = super.getRequestPath();

                if (mapping.charAt(0) == '/') {
                    return path.replaceFirst(mapping, "");
                }
                else if (path.contains("?")) {
                    return path.replace(mapping + "?", "?");
                }
                else {
                    return path.substring(0, path.length() - mapping.length());
                }
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getResourceName() {
                return resource.getResourceName();
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getLibraryName() {
                return resource.getLibraryName();
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getContentType() {
                return resource.getContentType();
            }

            @Override
            public Resource getWrapped() {
                return resource;
            }
        };
    }

    @Override
    public boolean isResourceRequest(FacesContext context) {
        return ResourceHandler.RESOURCE_IDENTIFIER.equals(context.getExternalContext().getRequestServletPath());
    }

    @Override
    public void handleResourceRequest(FacesContext context) throws IOException {
        ExternalContext externalContext = context.getExternalContext();
        String resourceName = externalContext.getRequestPathInfo();
        String libraryName = externalContext.getRequestParameterMap().get("ln");
        Resource resource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);

        if (resource == null) {
            super.handleResourceRequest(context);
            return;
        }

        if (!resource.userAgentNeedsUpdate(context)) {
            externalContext.setResponseStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        externalContext.setResponseContentType(resource.getContentType());

        for (Entry<String, String> header : resource.getResponseHeaders().entrySet()) {
            externalContext.setResponseHeader(header.getKey(), header.getValue());
        }

        ReadableByteChannel input = null;
        WritableByteChannel output = null;

        try {
            input = Channels.newChannel(resource.getInputStream());
            output = Channels.newChannel(externalContext.getResponseOutputStream());

            for (ByteBuffer buffer = ByteBuffer.allocateDirect(10240); input.read(buffer) != -1; buffer.clear()) {
                output.write((ByteBuffer) buffer.flip());
            }
        }
        finally {
            if (output != null) try { output.close(); } catch (IOException ignore) {}
            if (input != null) try { input.close(); } catch (IOException ignore) {}
        }
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}

Register it as follows in faces-config.xml:

注册如下faces-config.xml

<application>
    <resource-handler>com.example.UnmappedResourceHandler</resource-handler>
</application>

Extend the FacesServletURL pattern with ResourceHandler.RESOURCE_IDENTIFIER:

使用以下命令扩展FacesServletURL 模式ResourceHandler.RESOURCE_IDENTIFIER

<servlet-mapping>
    <servlet-name>facesServlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
    <url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>

回答by chkal

You could have a look at Rewrite. Rewrite allows to modify URLs that are rendered to the page and modify them in any way you want. You could do something like this to add a CDN To your site:

你可以看看Rewrite。重写允许修改呈现到页面的 URL,并以您想要的任何方式修改它们。您可以执行以下操作将 CDN 添加到您的站点:

.addRule(CDN.relocate("{p}foo-{version}.css")
         .where("p").matches(".*")
         .where("version").matches(".*")
         .to("http://mycdn.com/foo-{version}.css"));

I think it should be easy to implement your requirement using Rewrite.

我认为使用 Rewrite 实现您的要求应该很容易。

Have a look at the example configurationsto learn about the features of rewrite.

查看示例配置以了解重写的功能。