Probablemente no sea tanto un error como una limitación de las especificaciones del Servlet. Los detalles de cómo se maneja una JAX-RS @ApplicationPath
son específicos de la implementación, y no puedo hablar de todas las implementaciones, pero supongo que el enfoque típico es simplemente usarlo como un patrón de URL de servlet. Echando un vistazo a la implementación ServletContainerInitializer de Jersey como un ejemplo, encontrará que el addServletWithApplication()
method es responsable de crear el servlet y el mapeo para manejar las solicitudes, y puede ver que sí, de hecho, usa la ruta del @ApplicationPath
como Jersey ServletContainer's ruta mapeada.
Desafortunadamente, desde tiempos inmemoriales, la especificación de Servlet ha permitido solo un pequeño puñado de maneras de asignar servlets a rutas de URL. Las opciones actuales con Servlet 3.0, dados en Section 12.2 of the spec --sadly sólo está disponible en formato PDF, por lo que no vinculable por sección - son:
/.../*
donde el /...
inicial es cero o más elementos de ruta
*.<ext>
donde <ext>
es un poco de extensión para que coincida con
- la cadena vacía, que asigna sólo a la raíz vacío ruta/contexto
/
, la sola barra, lo que indica que el servlet "default" en el contexto, que se ocupa de todo lo que no hace emparejar cualquier otra cosa
- cualquier otra cadena, que se trata como un valor literal para que coincida con
La misma sección de la especificación también tiene normas específicas para el orden en que deben aplicarse las reglas de coincidencia, pero la versión corta es la siguiente: Para realizar las solicitudes de respuesta de clase de recurso en la raíz de contexto, debe usar /
o /*
como ruta.Si usa /
, está reemplazando el servlet predeterminado del contenedor, que normalmente sería responsable de manejar los recursos estáticos. Si usa /*
, entonces lo está haciendo demasiado codicioso y diciendo que debe coincidir todo todo el tiempo, y el servlet predeterminado nunca será invocado.
Así que si aceptamos que estamos dentro del cuadro determinado por las limitaciones de los patrones de servlet URL, nuestras opciones son bastante limitadas. Aquí están los que puedo pensar:
1) Use @ApplicationPath("/")
, y explíqueme explícitamente sus recursos estáticos por nombre o por extensión al servlet predeterminado del contenedor (denominado "predeterminado" en Tomcat y Jetty, no estoy seguro de los demás). En un web.xml, que se vería así
<!-- All html files at any path -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- Specifically index.html at the root -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
o con un ServletContextInitializer, como
public class MyInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx) {
ctx.getServletRegistration("default").addMapping("*.html");
ctx.getServletRegistration("default").addMapping("/index.html");
}
}
Debido a la forma en que las reglas de concordancia se escriben, un patrón de extensión gana sobre el servlet por defecto, por lo solo necesitaría agregar una asignación por extensión de archivo estático, siempre que no haya superposición entre esas y las "extensiones" que puedan ocurrir en su API. Esto está muy cerca de la opción indeseable mencionada en la publicación del foro que vinculó, y solo la menciono para completarla y para agregar la parte de ServletContextInitializer.
2) Deje su API asignada al /rest/*
, y use un filtro para identificar las solicitudes de la API y reenviarlas a esa ruta. De esta forma, saldrá del cuadro de patrón de URL del servlet y podrá hacer coincidir las URL de la forma que desee. Por ejemplo, suponiendo que todas sus llamadas REST son caminos que comiencen con la letra "/ foo" o son exactamente "/ bar" y todas las demás solicitudes deben ir a los recursos estáticos, a continuación, algo así como:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.regex.Pattern;
@WebFilter(urlPatterns = "/*")
public class PathingFilter implements Filter {
Pattern[] restPatterns = new Pattern[] {
Pattern.compile("/foo.*"),
Pattern.compile("/bar"),
};
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
String path = ((HttpServletRequest) request).getServletPath();
for (Pattern pattern : restPatterns) {
if (pattern.matcher(path).matches()) {
String newPath = "/rest/" + path;
request.getRequestDispatcher(newPath)
.forward(request, response);
return;
}
}
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
Con el anterior, se traduce esencialmente solicitudes de la siguiente manera:
http://example.org/foo -> http://example.org/rest/foo
http://example.org/foox -> http://example.org/rest/foox
http://example.org/foo/anything -> http://example.org/rest/foo/anything
http://example.org/bar -> http://example.org/rest/bar
http://example.org/bart -> http://example.org/bart
http://example.org/index.html -> http://example.org/index.html
3) dan cuenta que la opción anterior es básicamente la reescritura de URL y utiliza una aplicación existente, como Apache's mod_rewrite, la Tuckey rewrite filter, o ocpsoft Rewrite.
No veo cómo esto se ocupa de la cuestión. Pero aquí está el blog del que proviene el código de esta respuesta, si alguien tiene curiosidad: http://buraktas.com/resteasy-example-without-using-a-web-xml/ – Amalgovinus
Lo escribí según mi experiencia. Su enlace no tiene ningún código que crea que sea el mismo para mi código aquí. Mi código es una plantilla genérica, no un código propietario específico. Si desea publicitar, este no es el lugar correcto para hacerlo. –