java - usar - ¿Alguna forma ingeniosa de manejar el contexto en una aplicación web?
tutorial interfaz grafica java netbeans (14)
La API de Servlet y JSP tienen instalaciones para ayudar a administrar esto. Por ejemplo, si, en un servlet, usted hace: response.sendRedirect ("/ mypage.jsp"), el contenedor antepondrá el contexto y creará la url: http://example.com/myapp/mypage.jsp ".
Ah, tal vez, tal vez no, ¡depende de tu contenedor y de las especificaciones del servlet!
De Servlet 2.3: Nuevas características expuestas :
Y finalmente, después de un prolongado debate por parte de un grupo de expertos, Servlet API 2.3 ha aclarado de una vez y para siempre exactamente lo que sucede en una llamada a res.sendRedirect ("/ index.html") para un servlet que se ejecuta dentro de un contexto no raíz. El problema es que Servlet API 2.2 requiere una ruta incompleta como "/index.html" para ser traducida por el contenedor de servlets a una ruta completa, pero no dice cómo se manejan las rutas de contexto. Si el servlet que realiza la llamada se encuentra en un contexto en la ruta "/ contextpath", el redireccionamiento del URI se debe traducir en relación con la raíz del contenedor ( http://server:port/index.html ) o la raíz del contexto ( http://server:port/contextpath/index.html )? Para una portabilidad máxima, es imperativo definir el comportamiento; después de un largo debate, los expertos eligieron traducir en relación con la raíz del contenedor. Para aquellos que quieren un contexto relativo, puede anteponer la salida de getContextPath () a su URI.
Entonces, no, con 2.3 tus rutas no se traducen automáticamente para incluir la ruta de contexto.
En Java, las aplicaciones web se incluyen en WAR. De manera predeterminada, muchos contenedores de servlets usarán el nombre WAR como el nombre de contexto para la aplicación.
Por lo tanto, myapp.war se implementa en http://example.com/myapp .
El problema es que la aplicación web considera que su "raíz" es, bueno, "raíz" o simplemente "/", mientras que HTML consideraría que la raíz de su aplicación es "/ myapp".
La API de Servlet y JSP tienen instalaciones para ayudar a administrar esto. Por ejemplo, si, en un servlet, usted hace: response.sendRedirect ("/ mypage.jsp"), el contenedor antepondrá el contexto y creará la url: http://example.com/myapp/mypage.jsp ".
Sin embargo, no puede hacer eso con, digamos, la etiqueta IMG en HTML. Si lo haces <img src = "/ myimage.gif" /> probablemente obtengas un 404, porque lo que realmente querías era "/myapp/myimage.gif".
Muchos frameworks también tienen etiquetas JSP que también tienen en cuenta el contexto, y hay diferentes formas de crear URL correctas dentro de JSP (ninguna particularmente elegante).
Es un problema esencial para los programadores saltar en un momento de cuándo utilizar una url "App Relative", frente a una url absoluta.
Finalmente, está la cuestión del código Javascript que necesita crear URL sobre la marcha, y URL integradas dentro de CSS (para imágenes de fondo y similares).
Tengo curiosidad por las técnicas que otros usan para mitigar y solucionar este problema. Muchos simplemente lo puntúan y lo codifican, ya sea en la raíz del servidor o en cualquier contexto que estén usando. Ya sé esa respuesta, eso no es lo que estoy buscando.
¿Qué haces?
Al crear un sitio desde cero, estoy de acuerdo con @Will: apunte a una estructura de url consistente y predecible para que pueda quedarse con las referencias relativas.
Pero las cosas pueden ser realmente complicadas si está actualizando un sitio que fue originalmente creado para trabajar directamente bajo el sitio raíz "/" (bastante común para sitios JSP simples) a un empaque formal de Java EE (donde la raíz de contexto será alguna ruta debajo de la raíz )
Eso puede significar muchos cambios de código.
Si desea evitar o posponer los cambios en el código, pero aún garantizar la correcta referencia de raíz de contexto, una técnica que he probado es usar filtros de servlet. El filtro se puede colocar en un proyecto existente sin cambiar nada (excepto web.xml) y reasignará las referencias de url en el HTML saliente a la ruta correcta, y también garantizará que los redireccionamientos estén referenciados correctamente.
Un ejemplo de sitio y código utilizable disponible aquí: EnforceContextRootFilter-1.0-src.zip NB: las reglas de asignación reales se implementan como expresiones regulares en la clase de servlet y proporcionan un EnforceContextRootFilter-1.0-src.zip bastante general, pero es posible que deba modificarlas para circunstancias particulares.
Por cierto, bifurqué una pregunta ligeramente diferente para abordar la migración de la base de código existente de "/" a una ruta de contexto no raíz
De ninguna manera afirmo que el siguiente es un tema elegante. De hecho, en retrospectiva, no recomendaría este tema dado el golpe de rendimiento (más probable).
Los JSP de nuestra aplicación web eran estrictamente datos brutos XML. Estos datos brutos se enviaron a un XSL (del lado del servidor) que aplicaba las etiquetas CSS correctas y escupía el XHTML.
Teníamos un template.xsl único que sería heredado por los múltiples archivos XSL que teníamos para diferentes componentes del sitio web. Nuestras rutas estaban todas definidas en un archivo XSL llamado paths.xml:
<?xml version="1.0" encoding="UTF-8"?>
<paths>
<path name="account" parent="home">Account/</path>
<path name="css">css/</path>
<path name="home">servlet/</path>
<path name="icons" parent="images">icons/</path>
<path name="images">images/</path>
<path name="js">js/</path>
</paths>
Un enlace interno estaría en el XML de la siguiente manera:
<ilink name="link to icons" type="icons">link to icons</ilink>
Esto sería procesado por nuestro XSL:
<xsl:template match="ilink">
<xsl:variable name="temp">
<xsl:value-of select="$rootpath" />
<xsl:call-template name="paths">
<xsl:with-param name="path-name"><xsl:value-of select="@type" /></xsl:with-param>
</xsl:call-template>
<xsl:value-of select="@file" />
</xsl:variable>
<a href="{$temp}" title="{@name}" ><xsl:value-of select="." /></a>
</xsl:template>
$rootPath
se pasó a cada archivo con ${applicationScope.contextPath}
La idea detrás de nosotros de usar XML en lugar de simplemente codificarla en un archivo JSP / Java era que no queríamos tener que volver a compilar.
Nuevamente, la solución no es buena en absoluto ... ¡pero la usamos una vez!
Editar : En realidad, la complejidad en nuestro problema surgió porque no pudimos usar JSP para nuestra vista completa. ¿Por qué alguien no usaría ${applicationScope.contextPath}
para recuperar la ruta del contexto? Funcionó bien para nosotros entonces.
Esta es la mejor manera: Filtro de redirección de contexto El filtro debe aplicarse en el punto de extensión anterior al partido y, por lo tanto, usar la anotación @PreMatching.
Los filtros que implementan esta interfaz deben anotarse con @Provider para ser descubiertos por el tiempo de ejecución de JAX-RS. Las instancias de filtro de solicitud de contenedor también pueden descubrirse y vincularse dinámicamente a métodos de recursos particulares.
Explicado con código de muestra:
Estoy de acuerdo con tardate . También he prestado atención a los filtros y he encontrado la solución para el proyecto UrlRewriteFilter . La configuración simple como la siguiente:
<rule>
<from>^.+/resources/(.*)$</from>
<to>/resources/$1</to>
</rule>
ayuda a reenviar todas las solicitudes de ruta de acceso * / resources a / resources (incluido el prefijo de ruta de contexto). Así que puedo simplemente poner todas mis imágenes y archivos CSS en la carpeta de recursos y continuar usando URL relativas en mis estilos para imágenes de fondo y otros casos.
Excepto en casos especiales, recomendaría no usar URL absolutas de esta manera. Nunca. Las URL absolutas son buenas para cuando otra aplicación web señala algo en su aplicación web. Internamente, cuando un recurso apunta a un segundo recurso en el mismo contexto, el recurso debe saber dónde vive, por lo que debe poder expresar una ruta relativa al segundo recurso.
Por supuesto, escribirá componentes modulares, que no conocen el recurso que los incluye. Por ejemplo:
/myapp/user/email.jsp:
Email: <a href="../sendmail.jsp">${user.email}</a>
/myapp/browse/profile.jsp:
<jsp:include page="../user/email.jsp" />
/myapp/home.jsp:
<jsp:include page="../user/email.jsp" />
Entonces, ¿cómo conoce email.jsp
la ruta relativa de sendmail.jsp
? Claramente, el enlace se romperá en /myapp/browse/profile.jsp
o se romperá en /myapp/home.jsp
. La respuesta es, mantenga todas sus URL en el mismo espacio de archivo plano. Es decir, cada URL no debería tener barras después de /myapp/
.
Esto es bastante fácil de realizar, siempre que tenga algún tipo de asignación entre las URL y los archivos reales que generan el contenido. (por ejemplo, en Spring, use DispatcherServlet para asignar direcciones URL a archivos JSP o a vistas).
Hay casos especiales. por ejemplo, si está escribiendo una aplicación del lado del navegador en Javascript, entonces es más difícil mantener un espacio de ruta de archivo plano. En ese caso, o en otros casos especiales, o simplemente si tiene una preferencia personal, no es realmente un gran problema usar <%= request.getContextPath() %>
para crear una ruta absoluta.
He usado clases de ayuda para generar etiquetas de img, etc. Esta clase de ayuda se encarga de prefijar las rutas con el path de contexto de la aplicación. (Esto funciona, pero realmente no me gusta. Si alguien tiene mejores alternativas, dígaselo).
Para rutas en archivos css, etc. utilizo un script de construcción Ant que usa un site.production.css para site.css en el entorno de producción y site.development.css en el entorno de desarrollo.
Alternativamente, a veces uso un script Ant que reemplaza @ token @ tokens con los datos adecuados para diferentes entornos. En este caso, @ contextPAth @ token se reemplazaría por la ruta de contexto correcta.
He usado la mayoría de estas técnicas (guarde la arquitectura XSLT).
Creo que el punto crucial (y el consenso) del problema es tener un sitio con directorios potencialmente múltiples.
Si la profundidad de su directorio (por falta de un término mejor) es constante, entonces puede confiar en las URL relativas en cosas como CSS.
Cuidado, el diseño no tiene que ser completamente plano, solo consistente.
Por ejemplo, hemos hecho jerarquías como / css, / js, / common, / admin, / user. Poniendo páginas y recursos apropiados en los directorios apropiados. Tener una estructura como esta funciona muy bien con la autenticación basada en contenedores.
También he mapeado * .css y * .js al servlet JSP, y los he hecho dinámicos para que pueda construirlos sobre la marcha.
Solo esperaba que hubiera algo más que me haya perdido.
Para páginas HTML, acabo de configurar la etiqueta HTML <base>
. Cada enlace relativo (es decir, que no comienza con el esquema o /
) se volverá relativo a él. No hay una forma clara de capturarlo inmediatamente por HttpServletRequest
, por lo que necesitamos poca ayuda de JSTL aquí.
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<c:set var="req" value="${pageContext.request}" />
<c:set var="url">${req.requestURL}</c:set>
<c:set var="uri">${req.requestURI}</c:set>
<!DOCTYPE html>
<html lang="en">
<head>
<base href="${fn:substring(url, 0, fn:length(url) - fn:length(uri))}${req.contextPath}/" />
<link rel="stylesheet" href="css/default.css">
<script src="js/default.js"></script>
</head>
<body>
<img src="img/logo.png" />
<a href="other.jsp">link</a>
</body>
</html>
Sin embargo, esto a su vez es una advertencia: los anclajes (los #identifier
URL) se volverán relativos a la ruta base también. Si tiene alguno de ellos, le gustaría hacerlo en relación con el URL de solicitud (URI) en su lugar. Entonces, cambie como
<a href="#identifier">jump</a>
a
<a href="${uri}#identifier">jump</a>
En JS, puede acceder al elemento <base>
desde DOM siempre que quiera convertir una URL relativa en una URL absoluta.
var base = document.getElementsByTagName("base")[0].href;
O si lo haces jQuery
var base = $("base").attr("href");
En CSS, las URL de imagen son relativas a la URL de la hoja de estilo en sí. Por lo tanto, simplemente suelte las imágenes en una carpeta relativa a la hoja de estilo en sí. P.ej
/css/style.css
/css/images/foo.png
y hacer referencia a ellos de la siguiente manera
background-image: url(''images/foo.png'');
Si prefieres dejar caer las imágenes en alguna carpeta al mismo nivel que la carpeta CSS
/css/style.css
/images/foo.png
luego use ../
para ir a la carpeta principal común
background-image: url(''../images/foo.png'');
Ver también:
Puede usar JSTL para crear urls.
Por ejemplo, <c:url value="/images/header.jpg" />
prefijará la raíz del contexto.
Con CSS, esto generalmente no es un problema para mí.
Tengo una estructura de raíz web como esta:
/ css
/ images
En el archivo CSS, solo necesita usar URL relativas (../images/header.jpg) y no necesita conocer la raíz del contexto.
En cuanto a JavaScript, lo que funciona para mí incluye algunos JavaScript comunes en el encabezado de página como este:
<script type="text/javascript">
var CONTEXT_ROOT = ''<%= request.getContextPath() %>'';
</script>
Luego puede usar la raíz de contexto en todos sus scripts (o, puede definir una función para crear rutas, puede ser un poco más flexible).
Obviamente, todo esto depende de que uses JSP y JSTL, pero utilizo JSF con Facelets y las técnicas involucradas son similares: la única diferencia real es obtener la raíz del contexto de una manera diferente.
Puede usar request.getContextPath () para compilar URL absolutas que no están codificadas en un contexto específico. Como indicaba una respuesta anterior, para JavaScript, simplemente establece una variable en la parte superior de su JSP (o preferiblemente en una plantilla) y prefija eso como el contexto.
Eso no funciona para la sustitución de imágenes CSS a menos que desee generar dinámicamente un archivo CSS, lo que puede causar otros problemas. Pero como sabe dónde está su archivo CSS en relación con sus imágenes, puede salirse con URL relativos.
Por alguna razón, he tenido problemas con IE al manejar direcciones URL relativas y tuve que recurrir al uso de expresiones con una variable de JavaScript configurada para el contexto. Simplemente dividí los reemplazos de imagen de IE en su propio archivo y usé las macros de IE para obtener las correctas. No fue un gran problema porque de todos modos tenía que hacer eso para tratar con PNG transparentes. No es bonito, pero funciona.
Tiendo a escribir una propiedad como parte de mi biblioteca principal de JavaScript dentro de mi. No creo que sea perfecto, pero creo que es lo mejor que he logrado.
En primer lugar, tengo un módulo que es parte del núcleo de mi aplicación que siempre está disponible
(function (APP) {
var ctx;
APP.setContext = function (val) {
// protect rogue JS from setting the context.
if (ctx) {
return;
}
val = val || val.trim();
// Don''t allow a double slash for a context.
if (val.charAt(0) === ''/'' && val.charAt(1) === ''/'') {
return;
}
// Context must both start and end in /.
if (val.length === 0 || val === ''/'') {
val = ''/'';
} else {
if (val.charAt(0) !== ''/'') {
val = ''/'' + val;
}
if (val.slice(-1) !== ''/'') {
val += ''/'';
}
}
ctx = val;
};
APP.getContext = function () {
return ctx || ''/'';
};
APP.getUrl = function (val) {
if (val && val.length > 0) {
return APP.getContext() + (val.charAt(0) === ''/'' ? val.substring(1) : val);
}
return APP.getContext();
};
})(window.APP = window.APP || {});
Luego uso mosaicos de apache con un encabezado común que siempre contiene lo siguiente:
<script type="text/javascript">
APP.setContext(''${pageContext.request[''contextPath'']}'');
// If preferred use JSTL cor, but it must be available and declared.
//APP.setContext(''<c:url value=''/''/>'');
</script>
Ahora que he inicializado el contexto, puedo usar getUrl(path)
desde cualquier lugar (archivos js o dentro de jsp / html) que devolverá una ruta absoluta para la cadena de entrada dada dentro del contexto.
Tenga en cuenta que los siguientes son equivalentes intencionalmente. getUrl
siempre devolverá una ruta absoluta ya que una ruta relativa no necesita que conozcas el contexto en primer lugar.
var path = APP.getUrl("/some/path");
var path2 = APP.getUrl("some/path");
Una opción es usar una estructura de aplicación "plana" y URL relativas siempre que sea posible.
Por "plano" me refiero a que no hay subdirectorios en la raíz de la aplicación, tal vez solo algunos directorios para contenido estático como "imágenes /". Todos los JSP, URL de acción, servlets van directamente debajo de la raíz.
Esto no resuelve completamente su problema, pero lo simplifica enormemente.
Vilmantas dijo la palabra correcta aquí: URL relativas.
Todo lo que necesita hacer en su IMG es usar
<img src="myimage.gif"/>
en lugar de
<img src="/myimage.gif"/>
y será relativo al contexto de la aplicación (como el navegador está interpretando la URL a la que debe dirigirse)