web-dev-qa-db-fra.com

Application JAX-RS sur le contexte racine - comment faire?

J'aimerais que mon application JAX-RX démarre au contexte racine afin que mes URL soient

http://example.com/restfullPath

et pas

http://example.com/rest/restfullPath

J'ai changé l'annotation de mon application à partir de ce

@ApplicationPath("/rest/*")

pour ça

@ApplicationPath("/*")

Mais il semble qu'il prenne en charge le service de fichiers tels que /index.html

Existe-t-il un moyen d'exécuter un JAX-RS sur le contexte de l'application racine tout en conservant des pages statiques?

Semble que c'était demandé avant sur le forum JBOSS, mais la solution n'est pas vraiment pratique

35
Eran Medan

Ce n'est probablement pas tant un bug qu'une limitation de la spécification Servlet. Les détails de la gestion d'un JAX-RS @ApplicationPath Sont spécifiques à l'implémentation, et je ne peux pas parler pour toutes les implémentations, mais je suppose que l'approche typique consiste à simplement l'utiliser comme modèle d'URL de servlet. En jetant un coup d'œil à l'implémentation ServletContainerInitializer de Jersey comme exemple, vous constaterez que la méthode addServletWithApplication() est responsable de la création de la servlet et du mappage pour gérer les demandes, et vous pouvez voir qu'elle utilise en effet le chemin de @ApplicationPath comme chemin mappé de Jersey ServletContainer.

Malheureusement, depuis des temps immémoriaux, la spécification Servlet n'a autorisé qu'une petite poignée de façons de mapper les servlets aux chemins URL. Les options actuelles avec Servlet 3.0, données dans Section 12.2 de la spécification - malheureusement uniquement disponible en format PDF, donc non liable par section - sont:

  • /.../* Où le /... Initial est zéro ou plusieurs éléments de chemin
  • *.<ext><ext> Est une extension à faire correspondre
  • la chaîne vide, qui ne correspond qu'au chemin/racine de contexte vide
  • /, La barre oblique unique, qui indique le servlet "par défaut" dans le contexte, qui gère tout ce qui ne correspond à rien d'autre
  • toute autre chaîne, qui est traitée comme une valeur littérale pour correspondre

La même section de la spécification a également des règles spécifiques pour l'ordre dans lequel les règles de correspondance doivent s'appliquer, mais la version courte est la suivante: pour que votre classe de ressources réponde aux demandes à la racine du contexte, vous devez utiliser soit / ou /* comme chemin. Si vous utilisez /, Vous remplacez le servlet par défaut du conteneur, qui serait normalement responsable de la gestion des ressources statiques. Si vous utilisez /*, Vous le rendez trop gourmand et vous dites qu'il doit tout correspondre en permanence, et la servlet par défaut ne sera jamais invoquée.

Donc, si nous acceptons que nous sommes à l'intérieur de la zone déterminée par les limites des modèles d'URL de servlet, nos options sont assez limitées. Voici ceux auxquels je peux penser:

1) Utilisez @ApplicationPath("/"), et mappez explicitement vos ressources statiques par nom ou par extension au servlet par défaut du conteneur (nommé "default" dans Tomcat et Jetty, pas sûr des autres). Dans un web.xml, cela ressemblerait à

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

ou avec un ServletContextInitializer , comme

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

En raison de la façon dont les règles de correspondance sont écrites, un modèle d'extension l'emporte sur le servlet par défaut, vous n'aurez donc qu'à ajouter un mappage par extension de fichier statique tant qu'il n'y a pas de chevauchement entre celles-ci et les "extensions" qui pourraient se produire dans votre API. C'est assez proche de l'option indésirable mentionnée dans le post du forum que vous avez lié, et je le mentionne juste pour être complet et pour ajouter la partie ServletContextInitializer.

2) Laissez votre API mappée sur /rest/* Et utilisez un filtre pour identifier les demandes de l'API et les transférer vers ce chemin. De cette façon, vous sortez de la zone de modèle d'URL de servlet et pouvez faire correspondre les URL comme vous le souhaitez. Par exemple, en supposant que tous vos appels REST sont vers des chemins qui commencent par "/ foo" ou sont exactement "/ bar" et que toutes les autres demandes doivent aller vers des ressources statiques, puis quelque chose comme:

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

Avec ce qui précède, vous traduisez essentiellement les demandes comme suit:

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) Réalisez que l'option précédente est essentiellement la réécriture d'URL et utilisez une implémentation existante, telle que mod_rewrite d'Apache , le filtre de réécriture Tuckey , ou ocpsoft Rewrite =.

41
Ryan Stewart

J'ai trouvé une autre solution qui implique des classes Jersey internes, je suppose que cela ne fait probablement pas encore partie de la spécification JAX-RS. (basé sur: 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);
    }
}

Cela semble fonctionner, mais je me demande si cela fait/fera partie de la spécification/implémentation JAX-RS.

2
Eran Medan

Vous pouvez essayer de rechercher DefaultServlet de votre conteneur de servlet et ajouter le mappage de servlet à la main dans web.xml pour gérer les fichiers de page tels que * .html, * .jsp ou tout autre.

Par exemple. pour Tomcat 5.5, il est décrit ici: http://Tomcat.Apache.org/Tomcat-5.5-doc/default-servlet.html .

1
yatul

Citant @damo pour Jersey 2.0 dans un autre article

"Alternativement, vous pourriez être en mesure de retirer quelque chose avec une sorte de redirection. Par exemple, avec un Filtre de pré-correspondance . Je n'ai jamais rien fait de tel, mais la documentation suggère que" vous peut même modifier l'URI de la demande "."

0
allegjdm93

Utilisez plutôt @ApplicationPath("/") (sans astérisque). Cela vous aidera dans votre cas.

Voici un exemple de service Web REST:

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

J'ai utilisé Eclipse pour exporter ce projet Web dynamique vers un fichier WAR nommé helloservice.war et je l'ai déployé sur WildFly qui s'exécutait sur ma machine locale. Son URL: http://localhost:8080/helloservice/hello.

En accédant à ce lien, il a renvoyé:

hello
0
Thach Van