JAX-RS在根上下文中的应用程序 - 如何完成? [英] JAX-RS Application on the root context - how can it be done?

查看:95
本文介绍了JAX-RS在根上下文中的应用程序 - 如何完成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想让我的JAX-RX应用程序从根上下文开始,所以我的URL将是



http://example.com/restfullPath



而不是



http://example.com/rest/restfullPath



我从此处切换了我的应用程序注释

  @ApplicationPath(/ rest / *)

到此

  @ApplicationPath(/ *)

但它似乎接管服务文件例如/index.html



有没有办法在根应用程序上下文中运行JAX-RS但仍然提供静态页面?



似乎这是之前询问过 JBOSS论坛,但解决方案并不实用

解决方案

这可能与Servlet规范的限制有关。如何处理JAX-RS @ApplicationPath 的细节是特定于实现的,我不能代表所有实现,但我猜想典型的方法是简单的将它用作servlet URL模式。以Jersey的ServletContainerInitializer实现为例,你会发现 addServletWithApplication()方法负责创建servlet和映射来处理请求,你可以看到它确实使用 @ApplicationPath 中的路径作为Jersey ServletContainer的映射路径。



<不幸的是,从远古时代开始,Servlet规范只允许少数几种将servlet映射到URL路径的方法。 Servlet 3.0的当前选项,在规范的第12.2节中给出 - 遗憾的是只能以PDF形式提供,因此不能按部分链接 - 是:




  • / .. ./*其中最初的 / ... 是零个或多个路径元素

  • *。< ext> 其中< ext> 是一些匹配的扩展程序

  • 空字符串,仅映射到空路径/上下文根

  • / ,单斜杠,表示默认上下文中的servlet,它处理任何与其他内容不匹配的内容

  • 任何其他字符串,被视为匹配的文字值



规范的同一部分对匹配规则应适用的顺序也有特定的规则,但短版本是这样的:在上下文中使您的资源类应答请求root,你要我们e / / * 作为路径。如果你使用 / ,那么你将替换容器的默认servlet,它通常负责处理静态资源。如果你使用 / * ,那么你就太贪婪了,并说它应该始终匹配所有的东西,并且永远不会调用默认的servlet。



因此,如果我们接受我们在由servlet URL模式的限制确定的框内,我们的选项相当有限。以下是我能想到的:



1)使用 @ApplicationPath(/),并明确通过名称或扩展名将静态资源映射到容器的默认servlet(在Tomcat和Jetty中命名为default,不确定其他人)。在web.xml中,它看起来像

 <! - 任何路径上的所有html文件 - > 
< servlet-mapping>
< servlet-name>默认< / servlet-name>
< url-pattern> * .html< / url-pattern>
< / servlet-mapping>
<! - 特别是root的index.html - >
< servlet-mapping>
< servlet-name>默认< / servlet-name>
< url-pattern> /index.html< / url-pattern>
< / servlet-mapping>

ServletContextInitializer ,如

 公共类MyInitializer实现ServletContainerInitializer {
public void onStartup(设置< Class<?>> c,ServletContext ctx){
ctx.getServletRegistration(default)。addMapping(*。html);
ctx.getServletRegistration(default)。addMapping(/ index.html);
}
}

由于匹配规则的编写方式,扩展模式胜过默认的servlet,因此您只需要为每个静态文件扩展名添加映射,只要这些扩展模块与API中可能出现的任何扩展之间没有重叠。这非常接近你链接的论坛帖子中提到的不受欢迎的选项,我只是提到它的完整性并添加了ServletContextInitializer部分。



2)离开你的API映射到 / rest / * ,并使用过滤器来标识API请求并将其转发到该路径。这样,您就可以打开servlet URL模式框,并以任何方式匹配URL。例如,假设您的所有REST调用都是以/ foo开头或完全是/ 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 =/ *)
公共类PathingFilter实现Filter {
Pattern [] restPatterns = new Pattern [] {
Pattern.compile( / foo.*),
Pattern.compile(/ bar),
};

@Override
public void doFilter(ServletRequest请求,ServletResponse响应,
FilterChain链)抛出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);
返回;
}
}
}
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的mod_rewrite Tuckey重写过滤器,或 ocpsoft重写


I would like to have my JAX-RX Application start at the root context so my URLs will be

http://example.com/restfullPath

and not

http://example.com/rest/restfullPath

I switched my Application's annotation from this

@ApplicationPath("/rest/*")

to this

@ApplicationPath("/*")

But then it seems that it takes over serving files such as /index.html

Is there a way to run a JAX-RS on the root application context but still have static pages served?

Seems this was asked before on the JBOSS forum, but the solution is not really practical

解决方案

It's probably not so much a bug as a limitation of the Servlet spec. The details of how a JAX-RS @ApplicationPath is handled is implementation specific, and I can't speak for all implementations, but I'd guess the typical approach is to simply use it as a servlet URL pattern. Taking a look at Jersey's ServletContainerInitializer implementation as one example, you'll find that the addServletWithApplication() method is responsible for creating the servlet and mapping to handle requests, and you can see that it does, indeed, use the path from the @ApplicationPath as the Jersey ServletContainer's mapped path.

Unfortunately, since time immemorial, the Servlet spec has allowed only a small handful of ways of mapping servlets to URL paths. The current options with Servlet 3.0, given in Section 12.2 of the spec--sadly only available as a PDF, so not linkable by section--are:

  • /.../* 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

The same section of the spec also has specific rules for the order in which the matching rules should apply, but the short version is this: to make your resource class answer requests at the context root, you have to use either / or /* as the path. If you use /, then you're replacing the container's default servlet, which would normally be responsible for handling static resources. If you use /*, then you're making it too greedy and saying it should match everything all the time, and the default servlet will never be invoked.

So if we accept that we're inside the box determined by the limitations of servlet URL patterns, our options are fairly limited. Here are the ones I can think of:

1) Use @ApplicationPath("/"), and explicitly map your static resources by name or by extension to the container's default servlet (named "default" in Tomcat and Jetty, not sure about others). In a web.xml, it would 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 a ServletContextInitializer, like

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

Because of the way the matching rules are written, an extension pattern wins over the default servlet, so you'd only need to add a mapping per static file extension as long as there's no overlap between those and any "extensions" that might occur in your API. This is pretty close to the undesirable option mentioned in the forum post you linked, and I just mention it for completeness and to add the ServletContextInitializer part.

2) Leave your API mapped to /rest/*, and use a Filter to identify requests for the API and forward them to that path. This way, you break out of the servlet URL pattern box and can match URLs any way you want. For example, assuming that all your REST calls are to paths that either begin with "/foo" or are 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 requests 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) Realize that the previous option is basically URL rewriting and use an existing implementation, such as Apache's mod_rewrite, the Tuckey rewrite filter, or ocpsoft Rewrite.

这篇关于JAX-RS在根上下文中的应用程序 - 如何完成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆