2009-08-05 20 views
55

Si mapeo mi aplicación de primavera para procesar todas las solicitudes entrantes ('/ *'), las solicitudes de contenido estático devuelven 404's. Por ejemplo, una solicitud para "myhost.com/css/global.css" devolvería un 404, aunque el recurso existe cuando Spring intercepta la solicitud.¿Se puede configurar SpringMVC para procesar todas las solicitudes, pero excluir directorios de contenido estático?

La alternativa es asignar SpringMVC a un subdirectorio (por ejemplo '/ home/'), pero en este caso, debe pasar este directorio en todos los enlaces dentro de la aplicación. ¿Hay alguna manera de asignar SpringMVC a '/' y excluir un conjunto de directorios del procesamiento?

Mi configuración web.xml actual es:

<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> 

Idealmente me gustaría tener el mapeo ser algo como lo siguiente:

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

¿Es este tipo de cosas posible?

Respuesta

7

Si usted quiere hacer esto con la primavera única, es posible, pero un poco desordenado:

  1. ya sea que usted necesita utilizar un SimpleUrlHandlerMapping para el que puede especificar explícitamente patrones de URL que deben ser asignadas a los controladores O extiéndalo para admitir URL "ignorar" como "css/**".
  2. Tendrá que escribir su propia implementación HttpRequestHandler que consistiría básicamente en la llamada "getServletContext(). GetRequestDsipatcher(). Include()" para devolver el recurso solicitado tal como está.
  3. Deberá registrar ese controlador como predeterminadoHandler para el SimpleUrlHandlerMapping anterior.

Una vez hecho esto, todas las solicitudes que no se pueden mapear a sus controladores se reenviarán a su HttpRequestHandler y se servirán "tal cual".

+1

Actualmente estoy usando DefaultAnnotationHandlerMapping para hacer un mapeo basado en anotaciones de los controladores. En función de su sugerencia, la ampliaré y agregaré soporte para exclusiones basadas en rutas. Gracias por señalarme en la dirección correcta. –

+0

¡Respuesta impresionante! –

+0

En caso de que alguien lo omita, tenga en cuenta que existe una solución mucho mejor para Spring 3.x. ¡Mira la respuesta a continuación! –

2

¿Qué está utilizando para servir sus imágenes estáticas? Si se trata de Apache, puede configurar Apache para que no pase las solicitudes css/js a su servidor de aplicaciones.

Si está utilizando Tomcat que había puesto algo como esto en su httpd.conf:

JkUnMount /*.css webapp 

Donde 'webapp' es la entrada de sus workers.properties.

Lo siento, no puedo darte una solución de Spring puro, pero así es como lo hago.

+1

Este es un gran consejo, pero estoy buscando algo portátil; Entonces, cuando el WAR ensamblado se coloca en un contenedor de servlet, funcionará sin que sea necesaria una configuración externa. –

3

Una forma de hacerlo sería con filtros. Tendría que escribir un poco de código personalizado, pero no está mal. He aquí un ejemplo, si usted no quiere pasar * .css o * .js a su servlet de primavera:

web.xml:

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

Java clase:

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; 
    } 
} 

I no lo he probado, pero creo que funcionará.

EDIT: No hay ninguna funcionalidad de exclusión en la especificación del servlet. No creo que haya una buena manera de hacerlo en Spring, pero básicamente logra lo mismo en tu publicación.

EDIT 2: Si quiere poder cambiar fácilmente lo que se filtra, puede usar Spring para inyectar algo en el filtro durante el tiempo de ejecución.

EDIT 3: Me di cuenta de que si reenvía directamente al archivo, volverá a hacer el filtro y quedará atrapado en un ciclo infinito. Puede haber otra forma de hacer esto con filtros, pero honestamente no estoy seguro de qué se trata.

+0

como la solución +1 – Gurnard

0

¿Tiene una (s) extensión (es) coherente (s) para las solicitudes que desea procesar por el despachador Spring (creo que la mayoría de los ejemplos Spring usan un * .htm)? En ese caso, podría mapear a las extensiones que desea procesar, que omitirían sus archivos css y js.

De lo contrario, estoy de acuerdo con Nalandial, el enfoque de filtro es probablemente el mejor trabajo en este momento.

+0

Estoy usando URLs tranquilas, por lo que no hay extensión para nada en la aplicación, por lo que el mapeo de sufijos no funcionará para mí. –

27

Lo resolví al servir contenido estático a través del servlet 'predeterminado', que solo sirve el contenido para el cliente. Entonces mi web.xml se ve así:

<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 --> 

Espero que esto ayude.

+0

Encontré esta solución también, mis únicos problemas son: no creo que sea portátil yb: tuve problemas para mapear la ruta "/" a través de anotaciones. Si estos dos pudieran resolverse, esta sería la solución perfecta para mí. –

+0

Lo siento si llego tarde respondiendo, estaba fuera de la ciudad. Sí, probablemente tenga razón sobre el problema de la portabilidad. No tengo este problema porque estoy atrapado en Tomcat. Probablemente puedas incluir en la guerra el DefaultServlet. Acerca de la ruta "/" a través de la anotación, en mi configuración (IndexController anotado a nivel de método) funciona con @RequestMapping (value = "/"). Con los controladores anotados a nivel de clase, a veces necesitaba hacer una anotación de esta manera @RequestMapping (value = "") –

+0

Excelente solución si ejecutas una instancia de desarrollador y no quiero molestarme en configurar un servidor web para medios estáticos. –

-1

Es más limpio para usar UrlRewriteFilter para redirigir la solicitud a su servlet, aquí un ejemplo de 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> 

NOTAS:

  • Es importante el último <rule> se encuentra en la parte inferior de modo img, js , css se detectará primero
  • El <outbound-rule> es opcional y es solo para hacer que el
    <c:url value="/app/some" /> existente represente /some en lugar de /app/some
56

NOTA: esta respuesta se aplica a la primavera 3.0.4+ SOLO

(Por cierto, esta cuestión también se ha tratado aquí: Spring serving static content with mvc:resources, invalid xsd)

Mira la primavera Proyecto mvc-showcase en el Spring subversion samples repository. Muestra exactamente lo que desea hacer, a saber, que puede delinear recursos estáticos que no serán procesados ​​por el DisapatcherServlet. Ver archivo /mvc-showcase/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml. He aquí un fragmento de cómo manejar estas exclusiones, donde las JS, CSS y las imágenes están en la raíz de contexto de aplicaciones (con el espacio de nombres MVC asignada a mvc:

<!-- resources exclusions from servlet mapping --> 
<mvc:resources mapping="/css/**" location="/css/" /> 
<mvc:resources mapping="/images/**" location="/images/" /> 
<mvc:resources mapping="/js/**" location="/js/" /> 
+6

Nota: El proyecto Spring MVC Showcase se ha movido a github. Acceda aquí: https://github.com/SpringSource/spring-mvc-showcase – WarFox

+0

¿Cómo haría esto mismo en una configuración usando anotaciones? – Jackie

+0

nm http://stackoverflow.com/questions/14299149/how-to-use-spring-mvcs-mvcresources-tag-in-a-java-application-context – Jackie

0

Por lo general, los grandes sitios web prefieren utilizar otro servidor solo para manejar contenido estático Las solicitudes de contenido estático van a un servidor y la dinámica va a otro (con muelle, en este caso).

En muchos casos, el servidor Nginx (http://nginx.com/), un reciente y servidor muy rápido.

Pero esto no es trivial para hacer. Muchas configuraciones.

4

manera más simple para mí (si se utiliza una versión bastante tarde de primavera) es

<mvc:resources mapping="/**/*.js" location="/"/> 
<mvc:resources mapping="/**/*.css" location="/"/> 
... 
+1

Me gusta esta solución, ya que estoy agregando la primavera a una muy sitio antiguo que tiene contenido estático en todo el lugar. Todos los otros ejemplos que he encontrado no toman eso en cuenta. ¡Gracias! – Jason

0

utilizo ruta URL virtual para recuperar el recurso que necesito. Normalmente utilizo Spring MVC, por lo que no pude tener javascripts y css en la carpeta/WEB-INF/views. Se me ocurrió este servlet personalizado para SÓLO permitir el acceso a los archivos .js & .css en la carpeta/WEB-INF/views. En su caso, si mueve la carpeta/css y/js a una carpeta principal como/resource, entonces mi solución se aplicará a usted.

Puede cambiar el String url = "YOUR_RESOURCE_FOLDER"

Así, por ejemplo, ruta de acceso virtual puede ser algo como http://www.mysite.com/resources/path/path/app.js

el que se hará a mi/WEB-INF/views/ruta/ruta/app .js

web.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> 

servlet

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); 
      } 
     } 
    } 
} 
0

Si está utilizando la primavera y 3.0.4 anteriormente debe utilizar solution provided by atrain

De lo contrario, puede hacer esto sencilla cosa:

quizás has siguiente estructura de directorios estática desea servir:

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

Eclipse proyectos web dinámicos generará por omisión siguiente estructura: WebContent/WEB-INF. Mueva la carpeta public fuera de su directorio WEB-INF en el directorio WebContent.

En el lado del cliente

refieren sus archivos estáticos en la siguiente forma:

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

Aquí es mi reference.

2

Tengo el mismo problema y aquí es cómo lo resolví:

Lo siguiente fue agregado al archivo 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> 

se añade el siguiente texto a la Spring3 servlet archivo de definición de frijol MVC (como applicationContext.xml, el archivo que está configurado en web.xml como el contextConfigLocation.):

<mvc:annotation-driven /> 
<mvc:default-servlet-handler /> 
-1

En mi caso todo estuvo bien. Pero tengo un problema en un controlador

que era mi problema @RequestMapping (method = RequestMethod.GET)

y cambio para esto:

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

y funciona

mirada de un controlador que tiene mala @RequestMappgin y el cambio.

Cuestiones relacionadas