Перейти к основному содержимому

Подавайте статические ресурсы с помощью Spring

· 8 мин. чтения

1. Обзор

В этом руководстве будет показано, как обслуживать статические ресурсы с помощью Spring , используя конфигурацию XML и Java.

2. Использование Spring Boot

Spring Boot поставляется с предварительно настроенной реализацией ResourceHttpRequestHandler для облегчения обслуживания статических ресурсов.

По умолчанию этот обработчик обслуживает статическое содержимое из любых каталогов /static, /public, /resources и /META-INF/resources , которые находятся в пути к классам . Поскольку src/main/resources обычно находится в пути к классам по умолчанию, мы можем поместить туда любой из этих каталогов.

Например, если мы поместим файл about.html в каталог /static в нашем пути к классам, мы сможем получить доступ к этому файлу через http://localhost:8080/about.html . Точно так же мы можем добиться того же результата, добавив этот файл в другие упомянутые каталоги.

2.1. Пользовательские шаблоны пути

По умолчанию Spring Boot обслуживает весь статический контент в корневой части запроса /** . Несмотря на то, что это кажется хорошей конфигурацией по умолчанию, мы можем изменить ее с помощью свойства конфигурации spring.mvc.static-path-pattern .

Например, если мы хотим получить доступ к тому же файлу через http://localhost:8080/content/about.html, мы можем указать это в нашем application.properties:

spring.mvc.static-path-pattern=/content/**

В средах WebFlux мы должны использовать свойство spring.webflux.static-path-pattern .

2.2. Пользовательские каталоги

Подобно шаблонам пути, также можно изменить расположение ресурсов по умолчанию с помощью свойства конфигурации spring.web.resources.static-locations . Это свойство может принимать несколько расположений ресурсов, разделенных запятыми:

spring.web.resources.static-locations=classpath:/files/,classpath:/static-files

Здесь мы обслуживаем статическое содержимое из каталогов /files и /static-files внутри пути к классам. Более того, Spring Boot может обслуживать статические файлы вне пути к классам :

spring.web.resources.static-locations=file:/opt/files

Здесь мы используем сигнатуру файлового ресурса , file:/ , для обслуживания файлов с нашего локального диска.

3. XML-конфигурация

Если нам нужно пойти по старинке с конфигурацией на основе XML, мы можем эффективно использовать элемент mvc:resources , чтобы указать расположение ресурсов с помощью определенного общедоступного шаблона URL.

Например, следующая строка будет обслуживать все запросы на ресурсы, поступающие с общедоступным шаблоном URL, например « /resources/**», путем поиска в каталоге «/ resources/ » в корневой папке нашего приложения:

<mvc:resources mapping="/resources/**" location="/resources/" />

Теперь мы можем получить доступ к файлу CSS, как на следующей HTML-странице:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
<title>Home</title>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>

4. Обработчик ResourceHttpRequestHandler

Весна 3.1. представил ResourceHand lerRegistry для настройки ResourceHttpRequestHandler для обслуживания статических ресурсов из пути к классам, WAR или файловой системы. Мы можем настроить ResourceHandlerRegistry программно внутри нашего класса конфигурации веб-контекста.

4.1. Обслуживание ресурса, хранящегося в WAR

Чтобы проиллюстрировать это, мы будем использовать тот же URL-адрес, что и раньше, чтобы указать на myCss.css , но теперь фактический файл будет находиться в папке WAR webapp/resources , где мы должны размещать статические ресурсы при развертывании приложений Spring 3.1+. :

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}

Давайте немного проанализируем этот пример. Во-первых, мы настраиваем внешний путь URI, определяя обработчик ресурсов. Затем мы сопоставляем этот внешний путь URI с физическим путем, где фактически находятся ресурсы.

Мы можем, конечно, определить несколько обработчиков ресурсов, используя этот простой, но гибкий API.

Теперь следующая строка на html - странице даст нам ресурс myCss.css внутри каталога webapp/resources :

<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">

4.2. Обслуживание ресурса, хранящегося в файловой системе

Допустим, мы хотим обслуживать ресурс, хранящийся в каталоге /opt/files/, всякий раз, когда поступает запрос на общедоступный URL-адрес, соответствующий шаблону /files/** . Мы просто настраиваем шаблон URL и сопоставляем его с этим конкретным местом на диске:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/files/**")
.addResourceLocations("file:/opt/files/");
}

Для пользователей Windows аргумент, переданный в addResourceLocations для этого примера, будет « file:///C:/opt/files/ ».

Как только мы настроим расположение ресурса, мы можем использовать сопоставленный шаблон URL-адреса в нашем home.html для загрузки изображения, хранящегося в файловой системе:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
<title>Home</title>
</head>
<body>
<h1>Hello world!</h1>
<img alt="image" src="<c:url value="files/myImage.png" />">
</body>
</html>

4.3. Настройка нескольких местоположений для ресурса

Что, если мы хотим искать ресурс более чем в одном месте?

Мы можем включить несколько местоположений с помощью метода addResourceLocations . Список местоположений будет искаться по порядку, пока не будет найден ресурс:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/","classpath:/other-resources/");
}

Следующий запрос curl отобразит страницу Hello.html , хранящуюся либо в папке webappp/resources приложения, либо в папке other-resources в пути к классам:

curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html

5. Новые ResourceResolvers

Весна 4.1. предоставляет с новыми ResourcesResolvers различные типы преобразователей ресурсов, которые можно использовать для оптимизации производительности браузера при загрузке статических ресурсов. Эти преобразователи могут быть объединены в цепочку и кэшированы в браузере для оптимизации обработки запросов.

5.1. PathResourceResolver _ ``

Это простейший распознаватель, и его цель — найти ресурс по общедоступному шаблону URL. На самом деле, если мы не добавим ResourceResolver в ResourceChainRegistration , это будет преобразователь по умолчанию.

Давайте посмотрим на пример:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/","/other-resources/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}

На что следует обратить внимание:

  • Мы регистрируем PathResourceResolver в цепочке ресурсов как единственный в ней ResourceResolver . Мы можем обратиться к разделу 4.3. чтобы увидеть, как связать более одного ResourceResolver .
  • Обслуживаемые ресурсы будут кэшироваться в браузере на 3600 секунд.
  • Наконец, цепочка настроена с помощью метода resourceChain(true) .

Теперь HTML-код, который в сочетании с PathResourceResolver находит скрипт foo.js либо в папке webapp/resources , либо в папке webapp/other-resources :

<script type="text/javascript" src="<c:url value="/resources/foo.js" />">

5.2. EncodedResourceResolver _ ``

Этот сопоставитель пытается найти закодированный ресурс на основе значения заголовка запроса Accept-Encoding .

Например, нам может потребоваться оптимизировать пропускную способность, предоставляя сжатую версию статического ресурса с использованием кодирования содержимого gzip .

Чтобы настроить EncodedResourceResolver, нам нужно настроить его в ResourceChain так же, как мы настроили PathResourceResolver :

registry
.addResourceHandler("/other-files/**")
.addResourceLocations("file:/Users/Me/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new EncodedResourceResolver());

По умолчанию EncodedResourceResolver настроен на поддержку кодировок br и gzip .

Таким образом, следующий запрос curl получит заархивированную версию файла Home.html , расположенного в файловой системе в каталоге Users/Me/ :

curl -H  "Accept-Encoding:gzip" 
http://localhost:8080/handling-spring-static-resources/other-files/Hello.html

Обратите внимание, как мы устанавливаем для заголовка Accept-Encoding значение gzip. Это важно, потому что этот конкретный распознаватель сработает только в том случае, если содержимое gzip допустимо для ответа.

Наконец, обратите внимание, что, как и раньше, сжатая версия будет оставаться доступной в течение периода времени, в течение которого она кэшируется в браузере, что в данном случае составляет 3600 секунд.

5.3. Цепочка ResourceResolvers

Чтобы оптимизировать поиск ресурсов, ResourceResolvers могут делегировать обработку ресурсов другим распознавателям. Единственный преобразователь, который не может делегировать цепочку, — это PathResourceResolver, который мы должны добавить в конец цепочки.

На самом деле, если для resourceChain не задано значение true , то по умолчанию для обслуживания ресурсов будет использоваться только PathResourceResolver . Здесь мы связываем PathResourceResolver для разрешения ресурса, если GzipResourceResolver не удался:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/js/**")
.addResourceLocations("/js/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new GzipResourceResolver())
.addResolver(new PathResourceResolver());
}

Теперь, когда мы добавили шаблон /js/** в ResourceHandler , давайте включим ресурс foo.js , расположенный в каталоге webapp/js/ на нашей странице home.html :

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet" />
<script type="text/javascript" src="<c:url value="/js/foo.js" />"></script>
<title>Home</title>
</head>
<body>
<h1>This is Home!</h1>
<img alt="bunny hop image" src="<c:url value="files/myImage.png" />" />
<input type = "button" value="Click to Test Js File" onclick = "testing();" />
</body>
</html>

Стоит отметить, что начиная с Spring Framework 5.1 GzipResourceResolver устарел в пользу EncodedResourceResolver . Поэтому нам следует избегать его использования в будущем.

6. Дополнительная конфигурация безопасности

При использовании Spring Security важно разрешить доступ к статическим ресурсам. Нам нужно добавить соответствующие разрешения для доступа к URL-адресу ресурса:

<intercept-url pattern="/files/**" access="permitAll" />
<intercept-url pattern="/other-files/**/" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />

7. Заключение

В этой статье мы проиллюстрировали различные способы, которыми приложение Spring может обслуживать статические ресурсы.

Конфигурация ресурсов на основе XML — это устаревший вариант , который мы можем использовать, если пока не можем пойти по пути конфигурации Java.

Весна 3.1. вышел с базовой программной альтернативой через свой объект ResourceHandlerRegistry .

Наконец, новые готовые объекты ResourceResolvers и ResourceChainRegistration , поставляемые с Spring 4.1 . предлагает функции оптимизации загрузки ресурсов, такие как кэширование и связывание обработчиков ресурсов, для повышения эффективности обслуживания статических ресурсов.

Как всегда, полный пример доступен на Github . Кроме того, в этом проекте также доступны исходные коды, относящиеся к Spring Boot .