Как обрабатывать статический контент в Spring MVC?
Я разрабатываю веб-приложение с использованием Spring MVC 3 и имею DispatcherServlet
перехватывать все запросы в '/' примерно так (web.xml):
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Теперь это работает как рекламируется, однако, как я могу обрабатывать статический контент? Ранее, перед использованием RESTful URL, я бы, например, перехватил все *.html и отправил их DispatcherServlet
, но теперь это другая игра с мячом.
У меня есть папка / static /, в которую входят / styles/, / js/, / images / etc, и я бы хотел исключить /static/* из DispatcherServlet
,
Теперь я мог заставить статические ресурсы работать, когда я сделал это:
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/</url-pattern>
</servlet-mapping>
Но я хочу, чтобы у него были хорошие URL-адреса (смысл использования Spring MVC 3), а не целевой страницы www.domain.com/app/
Я также не хочу, чтобы решение связывалось с tomcat или любым другим контейнером сервлетов, и поскольку это (относительно) низкий трафик, мне не нужен веб-сервер (например, apache httpd).
Есть ли чистое решение для этого?
22 ответа
Поскольку я потратил много времени на эту проблему, я решил поделиться своим решением. Начиная с весны 3.0.4, есть параметр конфигурации, который называется <mvc:resources/>
(подробнее об этом на веб-сайте справочной документации), который можно использовать для обслуживания статических ресурсов, все еще используя DispatchServlet в корне вашего сайта.
Чтобы использовать это, используйте структуру каталогов, которая выглядит следующим образом:
src/
springmvc/
web/
MyController.java
WebContent/
resources/
img/
image.jpg
WEB-INF/
jsp/
index.jsp
web.xml
springmvc-servlet.xml
Содержимое файлов должно выглядеть так:
SRC / SpringMVC / веб / HelloWorldController.java:
package springmvc.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping(value="/")
public String index() {
return "index";
}
}
WebContent / WEB-INF / web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
WebContent / WEB-INF / SpringMVC-servlet.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- not strictly necessary for this example, but still useful, see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-controller for more information -->
<context:component-scan base-package="springmvc.web" />
<!-- the mvc resources tag does the magic -->
<mvc:resources mapping="/resources/**" location="/resources/" />
<!-- also add the following beans to get rid of some exceptions -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
</bean>
<!-- JSTL resolver -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
WebContent / JSP / index.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1>Page with image</h1>
<!-- use c:url to get the correct absolute path -->
<img src="<c:url value="/resources/img/image.jpg" />" />
Надеюсь это поможет:-)
Эта проблема решена весной 3.0.4. Выпуск, где вы можете использовать <mvc:resources mapping="..." location="..."/>
элемент конфигурации в файле конфигурации вашего весеннего диспетчера.
Проверьте документацию Spring
В Spring 3.0.x добавьте следующее в ваш servlet-config.xml (файл, который настроен в web.xml как contextConfigLocation. Вам также нужно добавить пространство имен mvc, но просто Google для этого, если вы не знаете, как!;)
Это подходит для меня
<mvc:default-servlet-handler/>
С уважением
Аюб Малик
Если я правильно понимаю вашу проблему, я думаю, что нашел решение вашей проблемы:
У меня возникла та же проблема, когда был показан необработанный вывод без стилей CSS, javascripts или jquery-файлов.
Я просто добавил сопоставления в сервлет по умолчанию. В файл web.xml было добавлено следующее:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
Это должно отфильтровать запросы файлов javascript и css от объекта DispatcherRequest.
Опять же, не уверен, что это то, что вы ищете, но это сработало для меня. Я думаю, что "default" - это имя сервлета по умолчанию в JBoss. Не слишком уверен, что это для других серверов.
Есть еще одно сообщение о переполнении стека, в котором есть отличное решение.
Это не похоже на Tomcat, просто и отлично работает. Я попробовал несколько решений в этом посте с помощью Spring mvc 3.1, но затем возникли проблемы с доставкой моего динамического контента.
Вкратце, он говорит, что добавьте отображение сервлета следующим образом:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/images/*</url-pattern>
</servlet-mapping>
Я только что столкнулся с этой проблемой в Spring MVC 3.0, и я изначально использовал опцию UrlRewriteFilter. Однако я не был доволен этим решением, так как оно "не чувствуется правильным" (я не единственный - см. Ссылку выше на весенние форумы, где слово "взломать" появляется несколько раз).
Поэтому я предложил решение, аналогичное "Неизвестному (Google)", описанному выше, но позаимствовал идею о том, чтобы весь статический контент подавался из /static/ (взят из версии приложения Pet Store для Spring Roo). Сервлет "по умолчанию" не работал для меня, но Spring Webflow ResourceServlet работал (также взят из приложения, сгенерированного Spring Roo).
web.xml:
<servlet>
<servlet-name>mainDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mainDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
Единственное изменение, которое я внес в JSP, было добавление /static/ path к URL для CSS, JS и изображений. Например, "${pageContext.request.contextPath}/static/css/screen.css".
для пользователей Maven зависимость для "org.springframework.js.resource.ResourceServlet":
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>org.springframework.js</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
Я нашел способ обойти это с помощью urlrewritefilter. Пожалуйста, не стесняйтесь дать лучший ответ, если у вас есть!
В web.xml:
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
В urlrewrite.xml:
<urlrewrite default-match-type="wildcard">
<rule>
<from>/</from>
<to>/app/</to>
</rule>
<rule match-type="regex">
<from>^([^\.]+)$</from>
<to>/app/$1</to>
</rule>
<outbound-rule>
<from>/app/**</from>
<to>/$1</to>
</outbound-rule>
Это означает, что любой URI с "." в нем (например, style.css) переписываться не будет.
Мой собственный опыт с этой проблемой заключается в следующем. Кажется, что большинство веб-страниц и книг, связанных с Spring, предлагают следующий синтаксис:
<mvc:resources mapping="/resources/**" location="/resources/" />
Приведенный выше синтаксис предполагает, что вы можете поместить свои статические ресурсы (CSS, JavaScript, изображения) в папку с именем "resources" в корне вашего приложения, т.е. /webapp/resources/.
Однако, по моему опыту (я использую Eclipse и плагин Tomcat), единственный подход, который работает, - это если вы поместите папку ресурсов в WEB_INF (или META-INF). Итак, синтаксис, который я рекомендую, следующий.
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
В вашем JSP (или аналогичном) ссылайтесь на ресурс следующим образом.
<script type="text/javascript"
src="resources/my-javascript.js">
</script>
Излишне упоминать, что весь вопрос возник только потому, что я хотел, чтобы мой сервлет-диспетчер Spring (фронт-контроллер) перехватывал все, все динамическое, то есть. Таким образом, у меня есть следующее в моем web.xml.
<servlet>
<servlet-name>front-controller</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<!-- spring automatically discovers /WEB-INF/<servlet-name>-servlet.xml -->
</servlet>
<servlet-mapping>
<servlet-name>front-controller</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Наконец, так как я использую текущие лучшие практики, у меня есть следующее в моем сервлете фронт-контроллера xml (см. Выше).
<mvc:annotation-driven/>
И у меня есть следующее в моей реальной реализации контроллера, чтобы убедиться, что у меня есть метод по умолчанию для обработки всех входящих запросов.
@RequestMapping("/")
Надеюсь, это поможет.
Я получил ту же проблему и нашел ответ Джорис очень полезным. Но дополнительно мне нужно добавить
<mvc:annotation-driven />
в файл конфигурации сервлета. Без этого сопоставление ресурсов не будет работать, и все обработчики перестанут работать. Надеюсь, это кому-нибудь поможет.
URLRewrite - это своего рода "взлом", если вы хотите так его назвать. Это сводится к тому, что вы заново изобретаете колесо; так как уже есть существующие решения. Еще одна вещь, которую нужно помнить, это Http-сервер = статический контент и сервер приложений = динамический контент (именно так они были спроектированы). Передавая соответствующие обязанности каждому серверу, вы максимизируете эффективность... но в настоящее время это, вероятно, является проблемой только в средах, критичных к производительности, и что-то вроде Tomcat, скорее всего, будет работать хорошо в обеих ролях большую часть времени; но, тем не менее, об этом нужно помнить.
Я использовал оба способа - urlrewrite и аннотации на основе Spring mvc 3.0.x и обнаружил, что подход на основе аннотаций является наиболее подходящим, то есть
<annotation-driven />
<resources mapping="/resources/**" location="/resources/" />
В случае urlrewrite, необходимо определить множество правил и некоторое время также получить исключение для класса not found для UrlRewriteFilter, поскольку для него уже предусмотрена зависимость. Я обнаружил, что это происходит из-за наличия транзитивной зависимости, так что снова один шаг будет увеличен и придется исключить эту зависимость из pom.xml, используя
<exclusion></exclusion> tags.
Таким образом, подход, основанный на аннотациях, будет хорошей сделкой.
Я решил это так:
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
Это работает на Tomcat и, конечно, Jboss. Однако, в конце концов, я решил использовать решение, которое предоставляет Spring (как упомянул rozky), которое гораздо более переносимо.
Начиная с весны 3 все ресурсы должны отображаться по-другому. Вам необходимо использовать тег, чтобы указать расположение ресурсов.
Пример:
<mvc:resources mapping="/resources/**" location="/resources/" />
Поступая таким образом, вы направляете сервлет диспетчера просматривать ресурсы каталога для поиска статического содержимого.
Я знаю, что есть несколько конфигураций для использования статического содержимого, но мое решение состоит в том, что я просто создаю большую папку веб-приложений внутри вашего кота. Это "массовое веб-приложение" обслуживает только весь статический контент без обслуживания приложений. Это безболезненное и простое решение для подачи статического содержимого в ваше реальное весеннее веб-приложение.
Например, я использую две папки веб-приложений на своем коте.
- springapp: он запускает только весеннее веб-приложение без статического содержимого, такого как imgs, js или css. (посвящен весенним приложениям.)
- ресурсы: он обслуживает только статическое содержимое без JSP, сервлета или любого другого java-приложения. (предназначено для статического содержимого)
Если я хочу использовать javascript, я просто добавляю URI для моего файла javascript.
EX> /resources/path/to/js/myjavascript.js
Для статических изображений я использую тот же метод.
EX> /resources/path/to/img/myimg.jpg
Наконец, я наложил "ограничение безопасности" на своего кота, чтобы заблокировать доступ к реальному каталогу. Я поместил пользовательский список "nobody" в ограничение, чтобы страница генерировала "403 запрещенную ошибку", когда люди пытались получить доступ к пути статического содержимого.
Пока это работает очень хорошо для меня. Я также заметил, что многие популярные сайты, такие как Amazon, Twitter и Facebook, используют разные URI для предоставления статического содержимого. Чтобы узнать это, просто щелкните правой кнопкой мыши на любом статическом контенте и проверьте его URI.
Мой способ решения этой проблемы - поместить все ваши действия с определенным префиксом, таким как "web" или "service", и настроить, чтобы все URL с этим префиксом были перехвачены DispatcherServlet.
Это сделало реальную работу в моем случае
в web.xml:
...
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/images/*</url-pattern>
<url-pattern>/css/*</url-pattern>
<url-pattern>/javascripts/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring-mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
...
Для конфигурации весны на основе Java вы можете использовать следующее
Использование ResourceHandlerRegistry, в котором хранятся регистрации обработчиков ресурсов для обслуживания статических ресурсов.
Дополнительная информация @ WebMvcConfigurerAdapter, которая определяет методы обратного вызова для настройки конфигурации на основе Java для Spring MVC, включенного через @EnableWebMvc.
@EnableWebMvc
@Configurable
@ComponentScan("package.to.scan")
public class WebConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static_resource_path/*.jpg").addResourceLocations("server_destination_path");
}
Я просто добавляю три правила перед весенним правилом по умолчанию (/**) в urlrewritefilter (tuckey) (urlrewrite.xml), чтобы решить проблему
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite default-match-type="wildcard">
<rule>
<from>/</from>
<to>/app/welcome</to>
</rule>
<rule>
<from>/scripts/**</from>
<to>/scripts/$1</to>
</rule>
<rule>
<from>/styles/**</from>
<to>/styles/$1</to>
</rule>
<rule>
<from>/images/**</from>
<to>/images/$1</to>
</rule>
<rule>
<from>/**</from>
<to>/app/$1</to>
</rule>
<outbound-rule>
<from>/app/**</from>
<to>/$1</to>
</outbound-rule>
</urlrewrite>
Это может быть достигнуто как минимум тремя способами.
Решения:
- выставить HTML как файл ресурсов
- поручить JspServlet также обрабатывать *.html запросы
- написать свой собственный сервлет (или передать другой существующий запрос сервлета в *.html).
Для полных примеров кода, как этого добиться, обратитесь к моему ответу в другом посте: Как отобразить запросы в HTML-файл в Spring MVC?
Поместите статическое содержимое, например, css,js в следующий путь
resources
->static
->css
->js
(or)
resources
->public
->css
->js
После знакомства и прохождения того же процесса принятия решений, описанного здесь, я решил пойти с предложением ResourceServlet, которое работает довольно хорошо.
Обратите внимание, что вы получите больше информации о том, как использовать webflow в процессе сборки maven, здесь: http://static.springsource.org/spring-webflow/docs/2.0.x/reference/html/ch01s05.html
Если вы используете стандартный центральный репозиторий Maven, то артефакт (в противоположность вышеупомянутому пакету источников):
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-js</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
Проблема с URLPattern
Измените ваш шаблон URL в вашем отображении сервлета с "/" на "/*"
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:default-servlet-handler/>
</beans>
и если вы хотите использовать конфигурацию на основе аннотаций, используйте приведенный ниже код
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}