【问题标题】:Restlet: can't locate static files through DirectoryRestlet:无法通过目录定位静态文件
【发布时间】:2015-02-27 22:39:48
【问题描述】:

事实证明,通过 Restlet 提供静态文件比想象的要难。我怀疑与上下文初始化有关的一些深层问题,但我可能是错的。基本上,我还不能通过Directory 获得任何实际内容,尽管所有路由和所有经典的restlet 都工作得很好。

核心问题看起来像它在日志消息中显示的那样:

WARNING: No client dispatcher is available on the context. Can't get the target URI: war:///

我尝试了许多不同的基本 URL,但似乎都没有任何作用。当我调试时,果然getClientDispatcher() 返回null。

当我(错误地)修改 Spring 初始化以使用原始上下文而不是子上下文时,我没有收到此警告,并且显示了目录列表,但它还显示了大约七条 SEVERE 消息,说明这是一个安全风险。

我的 Spring XML 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util.xsd">

<bean id="trackerComponent" class="org.restlet.ext.spring.SpringComponent">
    <property name="defaultTarget" ref="trackerApplication" />
</bean>

<bean id="trackerComponentChildContext" class="org.restlet.Context"> 
    <lookup-method name="createChildContext" bean="trackerComponent.context" /> 
</bean>

<bean id="trackerApplication" class="ca.uhnresearch.pughlab.tracker.application.TrackerApplication">
    <property name="root" ref="router" />
</bean>

<!-- Define the router -->
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
    <constructor-arg ref="trackerComponentChildContext" />
    <property name="attachments">
        <map>
            <entry key="/" value-ref="staticsDirectory" />
        </map>
    </property>
</bean>

<bean id="staticsDirectory" class="ca.uhnresearch.pughlab.tracker.resource.SpringDirectory">
    <constructor-arg ref="router" />
    <constructor-arg value="war:///" />
    <property name="listingAllowed" value="true" />
</bean>

...

我的 SpringDirectory 类只是一个包装器,用于提供来自父级 RouterContext,就像其他 Spring 助手一样,它似乎工作得很好:传递相同的上下文 - 而没有一个子级上下文有一个有效的getClientDispatcher()

如果需要,这里是web.xml 的一些相关部分。我不完全确定为什么我需要添加 FILE 以允许客户端访问(毕竟我想通过 Web 容器提供服务)但我也尝试使用 file: URL,这对我没有任何影响可以看到,除了我不得不在不寻常的地方添加额外的设置。

<servlet>
    <servlet-name>tracker</servlet-name>
    <servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class>
    <init-param>
        <param-name>org.restlet.clients</param-name>
        <param-value>HTTP HTTPS FILE</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>tracker</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

我非常感谢您提供这方面的建议——实际上,我在尝试显示 index.html 上花费的时间比我在所有 Restlets 上的总和还要多,因此非常欢迎任何想法或建议。

更新

Thierry 的回复很有帮助,但我仍有一些问题,仍然无法提供静态文件。唯一的变化是我没有test.CustomSpringServerServlet——而且我并不完全清楚我需要一个,因为默认情况下似乎接受了额外的init-param

当使用 Jetty JAR 文件 (mvn jetty:run-war) 运行时,我现在得到一个堆栈回溯:

WARNING: Exception or error caught in status service
java.lang.NoSuchMethodError: org.restlet.Context.getClientDispatcher()Lorg/restlet/Restlet;
at ca.uhnresearch.pughlab.tracker.restlets.DirectoryFactoryBean$1.getContext(DirectoryFactoryBean.java:16)
at org.restlet.Restlet.handle(Restlet.java:220)
at org.restlet.resource.Finder.handle(Finder.java:449)
at org.restlet.resource.Directory.handle(Directory.java:245)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
...

这个堆栈回溯很容易是我更新的结果——我现在使用的是 Restlets 2.3.1 和 Spring 3.1.4,因为它与之前的警告非常相似。

奇怪的是,我没有从mvn jetty:run 那里得到这个,自相矛盾的是,它现在在顶层提供目录列表,但仍然拒绝提供任何文件。这样就确认了 WAR 协议能够访问某些东西。 war:// 协议没有高度记录,所以我猜测它只是从战争内容中服务,而且它当然还不能做到这一点。

我仍然希望file:// 协议能够正常工作——老实说,任何静态文件的服务都很棒,我已经不在乎了。但是,尝试仍然会导致错误:

WARNING: The protocol used by this request is not declared in the list of client connectors. (FILE). In case you are using an instance of the Component class, check its "clients" property.

我不太明白,因为它在 web.xml 的客户列表中,而且没有其他任何地方可以说明。

事实证明这已经够难了,我觉得也许我应该整理一个 Github 存储库供人们使用。

【问题讨论】:

    标签: spring file static restlet


    【解决方案1】:

    编辑:我完全更新了我的答案以描述如何解决您的问题

    我对您的问题进行了更深入的调查,并且可以重现它。其实是在Spring中配置目录本身的时候出现的问题。实际上,它位于负责带客户端调度程序的上下文中。

    这里有更多细节。事实上,客户端协议WAR 是在默认或 Spring servlet 中创建 Restlet 组件时自动注册的。见方法ServerServlet#init(Component)。所以对应的客户端调度器是在组件的上下文中设置的。

    所以你需要在实例化它的时候在Directory的构造函数中传递这个上下文。我重新设计了 Spring 配置以展示如何解决您的问题:

    public class DirectoryFactoryBean implements FactoryBean<Directory> {
        private Component component;
    
        @Override
        public Directory getObject() throws Exception {
            Directory directory = new Directory(component.getContext(), "war:///") {
                @Override
                public Context getContext() {
                    // Display if the client dispatcher is correctly set!
                    System.out.println(">> getContext().getClientDispatcher() = "+super.getContext().getClientDispatcher());
    
                    return super.getContext();
                }
            };
            directory.setListingAllowed(true);
            return directory;
        }
    
        @Override
        public Class<?> getObjectType() {
            return Directory.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    
        public Component getComponent() {
            return component;
        }
    
        public void setComponent(Component component) {
            this.component = component;
        }
    }
    

    这是类的内容``:

    public class CustomSpringServerServlet extends SpringServerServlet {
        @Override
        protected void init(Application application) {
            super.init(application);
        }
    
        @Override
        protected void init(Component component) {
            super.init(component);
    
            ClientList list = component.getClients();
            for (Client client : list) {
                System.out.println(">> client = "+client);
            }
        }
    }
    

    这是web.xml文件中的配置:

    <web-app>
        <context-param>
            <param-name>org.restlet.application</param-name>
            <param-value>org.restlet.tutorial.MyApplication</param-value>
        </context-param>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/applicationContext.xml</param-value>
        </context-param>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <servlet>
            <servlet-name>tracker</servlet-name>
            <servlet-class>test.CustomSpringServerServlet</servlet-class>
            <init-param>
                <param-name>org.restlet.component</param-name>
                <param-value>myComponent</param-value>
            </init-param>
            <init-param>
                <param-name>org.restlet.clients</param-name>
                <param-value>HTTP HTTPS FILE</param-value>
            </init-param>
        </servlet>
    </web-app>
    

    下面是对应的Spring配置:

    <bean id="myComponent" class="org.restlet.ext.spring.SpringComponent">
        <property name="defaultTarget" ref="router" />
    </bean>
    
    <bean name="router" class="org.restlet.ext.spring.SpringRouter">
        <property name="attachments">
            <map>
                <entry key="/dir" value-ref="staticsDirectory" />
            </map>
        </property>
    </bean>
    
    <bean id="staticsDirectory" class="test.DirectoryFactoryBean">
        <property name="component" ref="myComponent" />
    </bean>
    

    希望对你有帮助, 蒂埃里

    【讨论】:

    • 刚刚尝试添加,但似乎没有任何区别。我仍然收到“警告:上下文中没有可用的客户端调度程序”,并将其添加到此设置中。无论如何,我实际上并不确定此设置的作用:我希望客户端使用 HTTP/S 连接到服务器,但我希望服务器访问战争中的资源。
    • 我重现了您的问题并找到了解决方法;-) 我完全更新了我的答案,以描述如何正确配置基于协议 war:// 的目录。希望对你有帮助。
    • 好吧,它还没有为我工作 :-( -- 我没有test.CustomSpringServerServlet。我将对这个问题进行更新编辑,因为我认为这已经向前推进了,但是需要更多关于现在正在发生的事情的详细信息。
    • 是的。我创建了这个类(SpringServerServlet 的子类)来检查客户端协议是否正确注册。这里没有更多了。我在上面的答案中添加了此类内容。
    • 好的,我可以确认我们已经成功了!我已经在github.com/morungos/restlet-spring-static-files 建立了一个小子集,它成功地提供静态文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-05
    • 2017-02-18
    • 2018-10-19
    • 2015-02-05
    • 2020-05-19
    • 2018-04-09
    • 1970-01-01
    相关资源
    最近更新 更多