JAX-RS application in the root context - how can this be done?

I want the JAX-RX application to run in the root context, so my urls will be

http://example.com/restfullPath

but not

http://example.com/rest/restfullPath

I switched the annotation of the application from this

@ApplicationPath("/rest/*") 

to that

 @ApplicationPath("/*") 

But then it seems like it is taking maintenance files such as /index.html

Is there a way to run JAX-RS in the root context of the application, but still have static pages?

It seems to have been asked before on the JBOSS forum, but the solution is actually not practical

+26
java jax-rs
Jun 03 2018-12-12T00:
source share
5 answers

This is probably not so much a mistake as a limitation of the Servlet specification. The information on how JAX-RS @ApplicationPath implementation-specific, and I can't speak for all implementations, but I assume a typical approach is to just use it as a servlet URL pattern. As an example, consider the implementation of the Jersey ServletContainerInitializer, you will find that the addServletWithApplication() method is responsible for creating the servlet and mapping for processing requests, and you can make sure that it really uses the path from @ApplicationPath as the route set by the Jersey ServletContainer.

Unfortunately, from time immemorial, the Servlet specification allows only a small number of ways to map servlets to URLs. The current parameters from servlet 3.0, given in Section 12.2 of the specification , are available only as PDF, therefore they are not related to each other in sections:

  • /.../* , where the initial /... is zero or more path elements
  • *.<ext> where <ext> is some extension to match
  • empty string that displays only empty pool / root context
  • / , the only slash that points to the default servlet in a context that handles anything that doesn't match anything else.
  • any other string that is treated as a literal value to match

In the same section of the specification, there are also certain rules for the order in which matching rules should be applied, but the short version is this: in order for your resource class response requests to be in the root of the context, you must use either / or /* as the path. If you use / , you replace the default servlet container, which is usually responsible for handling static resources. If you use /* , then you make it too greedy and say that it must match all the time, and the default servlet will never be called.

So, if we agree that we are inside the field defined by the constraints of the servlet URL patterns, our options are quite limited. Here are the ones I can think of:

1) Use @ApplicationPath("/") and explicitly map your static resources by name or extension to the default servlet in the container (with the name "default" in Tomcat and Jetty, not sure about others). In web.xml it will look like

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

or with ServletContextInitializer e.g.

 public class MyInitializer implements ServletContainerInitializer { public void onStartup(Set<Class<?>> c, ServletContext ctx) { ctx.getServletRegistration("default").addMapping("*.html"); ctx.getServletRegistration("default").addMapping("/index.html"); } } 

Due to the way matching rules are written, the extension template wins the servlet by default, so you only need to add the map for the static file extension if there is no match between them and any "extensions" in your API. This is pretty close to the unwanted option mentioned in the forum post that you linked, and I just mentioned it for completeness and added the ServletContextInitializer part.

2) Leave your API mapped to /rest/* and use a filter to identify API requests and forward them to this path. This way you exit the servlet URL pattern window and can match the URLs in any way. For example, if you assume that all your REST calls refer to paths that begin with "/ foo" or exactly "/ bar", and all other requests should go to static resources, then something like:

 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() {} } 

With the above, you essentially translate queries as follows:

 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) Understand that the previous version basically rewrites the URL and uses an existing implementation such as Apache mod_rewrite , Tuckey rewrite filter or ocpsoft Rewrite .

+29
May 25 '13 at 6:57
source share

You can try to find the DefaultServlet your servlet container and add a web.xml servlet mapping for it to process page files such as * .html, * .jsp or whatever.

eg. for Tomcat 5.5 described here: http://tomcat.apache.org/tomcat-5.5-doc/default-servlet.html .

+1
Jun 04 '12 at 11:18
source share

I found another solution that includes inner classes in Jersey, I assume that it is probably not yet part of the JAX-RS specification. (based on: http://www.lucubratory.eu/simple-jerseyrest-and-jsp-based-web-application/ )

web.xml

 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>jersey-rest-jsp-frame-1</display-name> <filter> <filter-name>jersey</filter-name> <filter-class> com.sun.jersey.spi.container.servlet.ServletContainer </filter-class> <init-param> <param-name> com.sun.jersey.config.property.JSPTemplatesBasePath </param-name> <param-value>/WEB-INF/jsp</param-value> </init-param> <init-param> <param-name> com.sun.jersey.config.property.WebPageContentRegex </param-name> <param-value> (/(image|js|css)/?.*)|(/.*\.jsp)|(/WEB-INF/.*\.jsp)| (/WEB-INF/.*\.jspf)|(/.*\.html)|(/favicon\.ico)| (/robots\.txt) </param-value> </init-param> </filter> <filter-mapping> <filter-name>jersey</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> 

WEB-INF / JSP / index.jsp

 <%@ page contentType="text/html; charset=UTF-8" language="java" %> <html> <body> <h2>Hello ${it.foo}!</h2> </body> </html> 

IndexModel.java

 package example; import com.sun.jersey.api.view.Viewable; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.util.HashMap; @Path("/") @Produces(MediaType.TEXT_HTML) public class IndexModel { @GET public Response root() { return Response.seeOther(URI.create("/index")).build(); } @GET @Path("index") public Viewable index(@Context HttpServletRequest request) { HashMap<String, String> model = new HashMap<String, String>(); model.put("foo","World"); return new Viewable("/index.jsp", model); } } 

This seems to work, but I wonder if it will / will be part of the JAX-RS specification / implementation.

+1
Jun 12 '13 at 23:13
source share

Quote @damo for Jersey 2.0 from another post

"Alternatively, you could remove something with some sort of redirection. For example, with the Pre-match Filter ." I have never done anything like this, but the documentation suggests that "you can even change the request URI."

0
Jun 26 '13 at 22:02
source share

Use @ApplicationPath("/") instead (without an asterisk). This will help in your case.

Here is an example REST web service:

1. JaxRsActivator.java

 package com.stackoverflow; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/") public class JaxRsActivator extends Application { } 

2. HelloService.java

 package com.stackoverflow; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/hello") public class HelloService { @GET @Produces(MediaType.TEXT_HTML) public String hello() { return "hello"; } } 

I used Eclipse to export this Dynamic Web project to a WAR file named helloservice.war and deployed it to WildFly, which worked on my local computer. Its URL is: http://localhost:8080/helloservice/hello .

When accessing this link, it returns:

 hello 
0
Aug 6 '15 at 17:19
source share



All Articles