Приложение JAX-RS в корневом контексте - как это можно сделать?

Я хотел бы, чтобы мое приложение JAX-RX запускалось в корневом контексте, чтобы мои URL-адреса были

http://example.com/restfullPath

и не

http://example.com/rest/restfullPath

Я переключил аннотацию моего приложения от этого

@ApplicationPath("/rest/*")

к этому

@ApplicationPath("/*")

Но тогда кажется, что он принимает обслуживающие файлы, такие как /index.html

Есть ли способ запустить JAX-RS в контексте корневого приложения, но статические страницы по-прежнему обслуживаются?

Кажется это былоспросил раньше на форуме JBOSS, но решение не очень практично

Ответы на вопрос(5)

Решение Вопроса

вероятно, не столько ошибка, сколько ограничение спецификации Servlet. Подробности о том, как работает JAX-RS@ApplicationPath  зависит от реализации, и я не могу говорить за все реализации, но я предполагаю, что типичный подход состоит в том, чтобы просто использовать его в качестве шаблона URL сервлета. Взяв в качестве примера реализацию ServletContainerInitializer на Джерси, вы обнаружите, чтоaddServletWithApplication() method отвечает за создание сервлета и отображение для обработки запросов, и вы можете видеть, что он действительно использует путь из@ApplicationPath в качестве пути пути ServletContainer на Джерси.

К сожалению, с незапамятных времен спецификация Servlet допускает лишь небольшое количество способов сопоставления сервлетов с URL-путями. Текущие параметры Servlet 3.0, приведенные вРаздел 12.2 спецификации- доступно только в формате PDF, поэтому нет ссылок по разделам - это:

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

В этом же разделе спецификации также есть определенные правила для порядка, в котором должны применяться соответствующие правила, но краткая версия такова: чтобы ваши классы ресурсов отвечали на запросы в корневом каталоге контекста, вы должны использовать либо/ или же/* как путь. Если вы используете/затем вы заменяете сервлет по умолчанию контейнера, который обычно отвечает за обработку статических ресурсов. Если вы используете/*затем вы делаете его слишком жадным и говорите, что оно должно все время совпадать, и сервлет по умолчанию никогда не будет вызываться.

Поэтому, если мы примем, что мы находимся внутри коробки, определяемой ограничениями шаблонов URL сервлета, наши возможности довольно ограничены. Вот те, о которых я могу думать:

1) Использование@ApplicationPath("/")и явным образом сопоставьте ваши статические ресурсы по имени или по расширению с сервлетом контейнера по умолчанию (с именем "default" в Tomcat и Jetty, но не уверены в других). В файле web.xml это будет выглядеть

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

или сServletContextInitializer, лайк

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

Из-за способа написания соответствующих правил шаблон расширения выигрывает у сервлета по умолчанию, так что вам нужно только добавлять сопоставление для каждого статического расширения файла, если между ними и любыми "расширениями" нет перекрытия. это может произойти в вашем API. довольно близко к нежелательному варианту, упомянутому в сообщении на форуме, на которое вы ссылаетесь, и я просто упомяну его для полноты и добавления части ServletContextInitializer.

2) Оставьте свой API сопоставленным с/rest/*и использовать фильтр для определения запросов к API и перенаправления их по этому пути. Таким образом, вы выходите из поля шаблона URL сервлета и можете сопоставлять URL-адреса любым удобным для вас способом. Например, если предположить, что все ваши вызовы REST относятся к путям, которые начинаются с & quot; / foo & quot; или точно "/ bar" и все остальные запросы должны идти к статическим ресурсам, тогда что-то вроде:

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

С учетом вышесказанного вы по существу переводите запросы следующим образом:

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) Поймите, что предыдущая опция в основном переписывает URL, и используйте существующую реализацию, такую какApache's mod_rewrite,Фильтр перезаписи Tuckey, или жеocpsoft Переписать.

& quot; В качестве альтернативы вы можете выполнить что-то с помощью некоторого перенаправления. Например, сФильтр предварительного соответствия, Я никогда не делал ничего подобного, но документация предполагает, что «вы даже можете изменить URI запроса».

DefaultServlet вашего сервлет-контейнера и добавьте сервлет-отображение для него руками вweb.xml обрабатывать файлы подкачки, такие как * .html, * .jsp или любые другие.

Например. для Tomcat 5.5 это описано здесь:http://tomcat.apache.org/tomcat-5.5-doc/default-servlet.html.

которое включает внутренние классы Джерси, я полагаю, что оно, вероятно, еще не является частью спецификации JAX-RS. (на основе: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);
    }
}

Кажется, это работает, но мне интересно, если это будет / будет частью спецификации / реализации JAX-RS.

@ApplicationPath("/") вместо (без звездочки). Это поможет в вашем случае.

Вот пример веб-службы 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";
    }
}

Я использовал Eclipse для экспорта этого проекта Dynamic Web в файл WAR с именемhelloservice.war и развернул его в WildFly, который работал на моей локальной машине. Его URL:http://localhost:8080/helloservice/hello.

При доступе по этой ссылке возвращается:

hello
 03 дек. 2016 г., 20:32
Я написал это на основе моего опыта. Ваша ссылка не содержит никакого кода, который, по вашему мнению, совпадает с моим кодом здесь. Мой код является общим шаблоном, а не конкретным проприетарным кодом. Если вы хотите рекламировать, это не то место, чтобы сделать это.
 06 июн. 2016 г., 22:58
Я не вижу, как это решает вопрос. Но вот блог с кодом этого ответа, если кому-то интересно:buraktas.com/resteasy-example-without-using-a-web-xml

Ваш ответ на вопрос