Можно ли настроить SpringMVC для обработки всех запросов, но исключить каталоги статического содержимого?

Если я сопоставляю свое весеннее приложение для обработки всех входящих запросов ('/*'), то запросы на статическое содержимое возвращают 404. Например, запрос «myhost.com/css/global.css» вернет 404, даже если ресурс существует, поскольку Spring перехватывает запрос.

Альтернативой является сопоставление SpringMVC с подкаталогом (например, '/home/'), но в этом случае вы должны передавать этот каталог во всех ссылках в приложении. Есть ли способ сопоставить SpringMVC с «/» и исключить набор каталогов из обработки?

Моя текущая конфигурация web.xml:

<servlet>
    <servlet-name>springApp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>springApp</servlet-name>
    <url-pattern>/home/*</url-pattern>
</servlet-mapping>

В идеале я хотел бы, чтобы отображение было примерно следующим:

 <servlet-mapping>
    <servlet-name>springApp</servlet-name>
    <url-pattern>/*</url-pattern>
    <exclude>/css/*,/js/*</exclude>
 </servlet-mapping>

Возможен ли такой тип вещей?


person Rich Kroll    schedule 05.08.2009    source источник


Ответы (13)


Если вы хотите сделать это только с помощью Spring, это возможно, но немного грязно:

  1. Вам нужно либо использовать SimpleUrlHandlerMapping, для которого вы можете явно указать шаблоны URL-адресов, которые должны быть сопоставлены с контроллерами, ИЛИ расширить его для поддержки «игнорирования» URL-адресов, таких как «css/**».
  2. Вам потребуется написать собственный HttpRequestHandler. реализация, которая в основном будет состоять из вызова "getServletContext().getRequestDsipatcher().include()" для возврата запрошенного ресурса как есть.
  3. Вам нужно будет зарегистрировать этот обработчик как defaultHandler для вышеуказанного SimpleUrlHandlerMapping.

Как только все это будет сделано, все запросы, которые не могут быть сопоставлены с вашими контроллерами, будут перенаправлены на ваш HttpRequestHandler и обслужены «как есть».

person ChssPly76    schedule 05.08.2009
comment
В настоящее время я использую DefaultAnnotationHandlerMapping для сопоставления контроллеров на основе аннотаций. Основываясь на вашем предложении, я собираюсь расширить его и добавить поддержку исключений на основе пути. Спасибо, что указали мне правильное направление. - person Rich Kroll; 06.08.2009
comment
Если кто-то пропустил это, обратите внимание, что для Spring 3.x существует гораздо лучшее решение. Проверьте ответ ниже! - person Alex Beardsley; 05.06.2013

ПРИМЕЧАНИЕ: этот ответ относится ТОЛЬКО к Spring 3.0.4+

(Кстати, этот вопрос также обсуждался здесь: Spring, обслуживающий статический контент с mvc:resources, неверный xsd)

Ознакомьтесь с проектом Spring mvc-showcase в репозитории образцов Subversion Spring. Он точно показывает, что вы хотите сделать, а именно то, что вы можете выделить статические ресурсы, которые не будут обрабатываться DisapatcherServlet. См. файл /mvc-showcase/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml. Вот фрагмент того, как я обрабатываю эти исключения, когда JS, CSS и изображения находятся в корне контекста приложения (с пространством имен MVC, сопоставленным с mvc:

<!-- resources exclusions from servlet mapping -->
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/images/**" location="/images/" />
<mvc:resources mapping="/js/**" location="/js/" />
person atrain    schedule 29.12.2010
comment
Примечание. Проект Spring MVC Showcase перемещен на github. Доступ к нему здесь: github.com/SpringSource/spring-mvc-showcase - person WarFox; 20.06.2012
comment
Как бы вы сделали то же самое в конфиге, используя аннотации? - person Jackie; 03.03.2014
comment
nm stackoverflow.com/questions/14299149/ - person Jackie; 03.03.2014

Я решил, отдавая статический контент через сервлет «по умолчанию», который просто отправляет контент клиенту. Итак, мой web.xml выглядит так:

<servlet>
    <servlet-name>MyApp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>MyApp</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping> <!-- The 'dynamic' content -->

<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>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping> <!-- The 'static' content -->

Надеюсь это поможет.

person Community    schedule 09.08.2009
comment
Я также столкнулся с этим решением - мои единственные проблемы с ним: а: я не верю, что оно переносимо и б: у меня были проблемы с отображением пути / с помощью аннотаций. Если бы эти две проблемы можно было решить, это было бы идеальным решением для меня. - person Rich Kroll; 12.08.2009
comment
Извините, если поздно отвечаю, меня не было в городе. Да, наверное, вы правы насчет переносимости. У меня нет этой проблемы, потому что я застрял на tomcat. Вероятно, вы можете включить в войну DefaultServlet. Что касается пути / через аннотацию, в моей конфигурации (индексный контроллер с аннотацией на уровне метода) он работает с @RequestMapping (значение = /). С аннотированными контроллерами на уровне класса иногда мне нужно было аннотировать таким образом @RequestMapping(value = ) - person ; 20.08.2009
comment
Отличное решение, если вы используете экземпляр разработчика и не хотите заморачиваться с настройкой веб-сервера для статических носителей. - person Filip Dupanović; 14.06.2011
comment
чего бы это ни стоило, я пробовал, и это работает с Tomcat 7 и Jetty 6. - person Brad Parks; 28.08.2012
comment
Кажется, это не работает в Glassfish 3.1.2.2 (сборка 5). Я получаю следующую ошибку во время развертывания: Здесь нет веб-компонента с именем по умолчанию. Есть предположения? - person Marsellus Wallace; 29.10.2012

Самый простой способ для меня (при использовании достаточно поздней версии Spring) -

<mvc:resources mapping="/**/*.js" location="/"/>
<mvc:resources mapping="/**/*.css" location="/"/>
...
person Daniel Egan    schedule 14.11.2012
comment
Мне нравится это решение, так как я добавляю spring на очень старый сайт, на котором везде есть статический контент. Все другие примеры, с которыми я сталкивался, не принимают это во внимание. Спасибо! - person Jason; 10.01.2013

Один из способов сделать это — использовать фильтры. Вам придется написать немного пользовательского кода, но это неплохо. Вот пример, если вы не хотите передавать файлы *.css или *.js в свой сервлет Spring:

веб.xml:

<filter-mapping>
    <filter-name>fileTypeFilter</filter-name>
    <filter-class>foo.FileTypeFilter</filter-class>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Java-класс:

public class FileTypeFilter implements Filter {
     public void init(FilterConfig conf) {
         // init logic here
     }

     public void destroy() {
        // release resources here
     }

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
          if(shouldExclude(req)) {
              chain.doFilter(req, res);
              //some logic so the request doesnt go to the servlet

              //maybe you could just forward
              //the request directly to the file getting accessed.  not sure if that would work
          }

          //file should be passed to the servlet; you can do some logic here
          //if you want         
     }
     private boolean shouldExclude(ServletRequest req) {
         if(req instanceof HttpServletRequest) {
             HttpServletRequest hreq = (HttpServletRequest) req;
             return (hreq.getRequestURI().endsWith(".css") ||
                     hreq.getRequestURI().endsWith(".js"));
         }
         return false;
    }
}

Я не проверял это, но я думаю, что это сработает.

EDIT: в спецификации сервлета нет функции исключения. Я не думаю, что есть хороший способ сделать это в Spring, но, по сути, он достигает того же в вашем посте.

РЕДАКТИРОВАТЬ 2: если вы хотите иметь возможность легко изменить то, что фильтруется, вы можете просто использовать Spring, чтобы внедрить что-то в фильтр во время выполнения.

РЕДАКТИРОВАНИЕ 3. Я только что понял, что если вы перейдете непосредственно к файлу, он снова выполнит фильтр, и вы попадете в бесконечный цикл. Возможно, есть другой способ сделать это с помощью фильтров, но я, честно говоря, не уверен, что это такое.

person Alex Beardsley    schedule 05.08.2009

Что вы используете для обслуживания ваших статических изображений? Если это Apache, вы можете настроить Apache так, чтобы он не передавал запросы css/js на ваш сервер приложений.

Если вы используете Tomcat, вы должны поместить что-то вроде этого в свой httpd.conf:

JkUnMount /*.css  webapp

Где «webapp» — это запись из ваших work.properties.

Извините, я не могу дать вам чистое решение Spring, но я делаю это так.

person Darren Greaves    schedule 05.08.2009
comment
Это отличный совет, но я ищу что-то портативное; Поэтому, когда собранный WAR помещается в контейнер сервлетов, он будет работать без необходимости внешней настройки. - person Rich Kroll; 05.08.2009

У меня возникла та же проблема, и вот как я ее решил:

В файл web.xml было добавлено следующее:

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
    <url-pattern>*.css</url-pattern>
    <url-pattern>*.ico</url-pattern>
    <url-pattern>*.png</url-pattern>
    <url-pattern>*.jpg</url-pattern>
    <url-pattern>*.htc</url-pattern>
    <url-pattern>*.gif</url-pattern>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*.htm</url-pattern>
</servlet-mapping>

В файл определения bean-компонента сервлета Spring3 MVC было добавлено следующее (например, applicationContext.xml, файл, настроенный в web.xml как contextConfigLocation.):

<mvc:annotation-driven /> 
<mvc:default-servlet-handler />
person Green Lei    schedule 22.09.2015

Есть ли у вас согласованные расширения для запросов, которые вы хотите обрабатывать диспетчером Spring (я полагаю, что в большинстве примеров Spring используется *.htm)? В этом случае вы можете сопоставить расширения, которые вы хотите обработать, что позволит обойти ваши файлы css и js.

В противном случае я бы согласился с Наландиалом, подход с фильтром, вероятно, является лучшим решением на данный момент.

person jridley    schedule 05.08.2009
comment
Я использую спокойные URL-адреса, поэтому в приложении нет расширения ни для чего, поэтому сопоставление суффиксов для меня не работает. - person Rich Kroll; 05.08.2009

Я использую виртуальный URL-адрес для получения нужного мне ресурса. Обычно я использую Spring MVC, поэтому у меня не может быть javascripts и css в папке /WEB-INF/views. Я придумал этот пользовательский сервлет, чтобы разрешить ТОЛЬКО доступ к файлам .js и .css в папке /WEB-INF/views. В вашем случае, если вы переместите папку /css и папку /js в родительскую папку, например /resource, тогда мое решение будет применимо к вам.

Вы можете изменить строку url = "YOUR_RESOURCE_FOLDER"

Так, например, виртуальный путь может быть чем-то вроде http://www.mysite.com/resources/path/path/app.js

Это будет отображаться в моем /WEB-INF/views/path/path/app.js

веб.xml

<servlet>
    <servlet-name>ResourceDispatcherServlet</servlet-name>
    <servlet-class>mywebapp.web.ResourceDispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>ResourceDispatcherServlet</servlet-name>
    <url-pattern>/resource/*</url-pattern>
</servlet-mapping>

сервлет

public class ResourceDispatcherServlet extends HttpServlet {

    public void init() throws ServletException {

    }

    public void doGet(HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {


        String servletPath = req.getServletPath();   // /resource
        String pathInfo = req.getPathInfo();         // /path/path/app.js

        String url = "/WEB-INF/views" + pathInfo;

        String lastPath = StringUtil.substringAfterLast(pathInfo, "/");
        String extension = StringUtil.substringAfterLast(lastPath, ".");

        try {
            RequestDispatcher dispatcher = null;
            if (!StringUtil.isEmpty(extension) && ("js".equals(extension) || "css".equals(extension))) {
                dispatcher = req.getRequestDispatcher(url);
            }

            if (dispatcher != null) {
                dispatcher.include(req, rsp);
            }
            else {
                rsp.sendError(404);
            }
        }
        catch (Exception e) {
            if (!rsp.isCommitted()) {
                rsp.sendError(500);
            }
        }
    }
}
person webpro2828    schedule 03.07.2013

Если вы используете Spring 3.0.4 и выше, вам следует использовать решение, предоставленное atrain

В противном случае вы можете сделать это просто:

возможно, у вас есть следующая статическая структура каталогов, которую вы хотите обслуживать:

WebContent
   |
   WEB-INF
     |
    public
        |
       css
        |
       js
        |
       img

Eclipse Динамические веб-проекты по умолчанию генерируют следующую структуру: WebContent/WEB-INF. Переместите папку public из каталога WEB-INF в каталог WebContent.

На стороне клиента

ссылайтесь на свои статические файлы следующим образом:

<link rel="stylesheet" type="text/css" href="public/css/mystyles.css">

Вот мой ссылка.

person Vikram    schedule 06.03.2014

В моем случае все было ок. Но у меня проблема в контроллере

это была моя проблема @RequestMapping (метод = RequestMethod.GET)

у изменить для этого:

@RequestMapping(value = "/usuario", method = RequestMethod.GET)

и это работает

найдите контроллер с плохим @RequestMappgin и измените его.

person user3193801    schedule 14.12.2015

Лучше использовать UrlRewriteFilter для перенаправления запроса на ваш сервлет, вот пример urlrewrite.xml

<urlrewrite>
    <rule>
        <from>^/img/(.*)$</from>
        <to>/img/$1</to>
    </rule>
    <rule>
        <from>^/js/(.*)$</from>
        <to>/js/$1</to>
    </rule>
    <rule>
        <from>^/css/(.*)$</from>
        <to>/css/$1</to>
    </rule>
    <rule>
        <from>^/(.*)$</from>
        <to>/app/$1</to>
    </rule>
    <outbound-rule>
        <from>/app/(.*)$</from>
        <to>/$1</to>
    </outbound-rule>
</urlrewrite>

ПРИМЕЧАНИЯ:

  • Важно, чтобы последний <rule> был внизу, чтобы img, js, css были перехвачены первыми.
  • <outbound-rule> является необязательным и предназначен только для того, чтобы существующий
    <c:url value="/app/some" /> отображал /some вместо /app/some.
person victor hugo    schedule 09.04.2010

Обычно крупные веб-сайты предпочитают использовать другой сервер только для обработки статического контента. Запросы статического контента отправляются на один сервер, а динамические — на другой (в данном случае с пружиной).

Во многих случаях сервер Nginx (http://nginx.com/), новый и очень быстрый сервер.

Но сделать это не тривиально. Много конфигураций.

person Vitor Braga    schedule 08.07.2012