如何获取不带上下文路径的请求URI?

152

request.getRequestURI()方法会返回带有上下文路径的 URI。

例如,如果应用程序的基础URL是http://localhost:8080/myapp/(即上下文路径为myapp),且我对http://localhost:8080/myapp/secure/users调用request.getRequestURI(),它将返回/myapp/secure/users

是否有办法仅获取此部分/secure/users,即不带上下文路径的 URI?


8个回答

178

如果你在一个前端控制器Servlet中,它被映射到类似/foo/*的前缀模式上,那么你可以直接使用HttpServletRequest#getPathInfo()方法。

String pathInfo = request.getPathInfo();
// ...
假设你的示例中的servlet映射为/secure/*,那么这将返回/users,它将是典型前端控制器servlet中唯一感兴趣的信息。
如果servlet被映射到后缀模式,如*.foo(但您的URL示例并未表明这种情况),或者当您实际上在过滤器中时(当要调用的servlet尚未确定时,因此getPathInfo()可能会返回null),则最好根据上下文路径的长度自己截取请求URI,使用通常的String方法:
HttpServletRequest request = (HttpServletRequest) req;
String path = request.getRequestURI().substring(request.getContextPath().length());
// ...

3
使用这个方法而不是getServletPath()是否有原因?我正在编写一个过滤器,发现getPathInfo()返回null,但是getServletPath()返回路径减去上下文(适合传递给请求分派器)。 - Jason C
@JasonC:如先前所述,如果前端控制器servlet未映射到前缀模式,则getPathInfo()会返回null。 - BalusC
是的。我的意思是:你为什么更喜欢使用getPathInfo而不是getServletPath呢?这里很多其他得分较高的答案也没有使用getServletPath,这让我对它产生了怀疑,所以我才会问这个问题。我正在开发一个servlet项目,想要提高自己的技能水平。 - Jason C
1
@JasonC:当你安装了像JSF或Spring MVC这样的基于servlet的MVC框架时,servlet路径可能会发生变化。它将代表MVC框架的内部路径(例如/foo.xhtml而不是/foo.jsf),而不是实际请求URI(即最终用户在浏览器地址栏中看到的URI)。在这种情况下,原始servlet路径可以作为请求属性使用,键为RequestDispatcher.FORWARD_SERVLET_PATH。无论如何,问题明确要求请求URI(就像浏览器地址栏中的URI),因此答案基于此。 - BalusC

89
request.getRequestURI().substring(request.getContextPath().length())

4
我认为这个答案比getPathInfo更好,因为getPathInfo可能为空或存在其他奇怪的情况。与您所做的一样,各种Spring代码都会执行getContextPath并从URI中删除它,而不是使用getPathInfo。 - Adam Gent

40

使用Spring,您可以完成以下操作:

String path = new UrlPathHelper().getPathWithinApplication(request);

1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - James
我们如何获取实际的请求映射URL?请在此处指导:https://stackoverflow.com/questions/60446807/how-to-get-the-current-request-mapping-url-configured-at-controller-layer-when-r - PAA

16

有时候getPathInfo()会返回null。在HttpServletRequest的文档中,有这样一段话:

如果没有额外的路径信息,此方法将返回null。

在过滤器中,我需要获取不包含上下文路径的文件路径,但是getPathInfo() 返回了null。所以我使用另一种方法:httpRequest.getServletPath()

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    String newPath = parsePathToFile(httpRequest.getServletPath());
    ...

}

9
如果您在Filter中使用request.getPathInfo(),则似乎总是会得到null(至少在jetty上是这样)。
这个简洁的无效错误和响应都暗示了我认为的问题:

https://issues.apache.org/bugzilla/show_bug.cgi?id=28323

我怀疑这与过滤器在servlet收到请求之前运行有关。这可能是容器错误,或者是我无法识别的预期行为。
但是,contextPath是可用的,所以fforws的解决方案即使在过滤器中也适用。我不喜欢手动执行此操作,但实现存在问题或。

5
一种方法是从请求URI中重置Servlet上下文路径。
String p = request.getRequestURI();
String cp = getServletContext().getContextPath();

if (p.startsWith(cp)) {
  String.err.println(p.substring(cp.length());
}

在这里阅读相关信息


0

简短回答

String requestUriWithinApp = req.getPathInfo() == null
        ? req.getServletPath()
        : req.getServletPath() + req.getPathInfo();
ServletPath获取调用servlet的URL部分。而PathInfo返回与客户端发送的URL关联的任何额外路径信息。根据客户端使用的URL,PathInfo可能为空。
为了获得更完整的URI,您还可以包括queryString。然后可以使用以下代码:
StringBuilder sb = new StringBuilder();
sb.append(req.getServletPath());
if (req.getPathInfo() != null) {
    sb.append(req.getPathInfo());
}
if (req.getQueryString() != null) {
    sb.append("?").append(req.getQueryString());
}

附加信息

请求URI的公式如下:

requestURI = contextPath + servletPath + pathInfo

根据您的问题,您不想使用contextPath,所以您只需要:

servletPath + pathInfo

例如,如果您有:

  • 一个位于/catalog的Web应用上下文和
  • 一个模式为/garden/*GardenServlet以及
  • 客户端发送的请求为/catalog/lawn/index.html
  • 那么pathInfo就是/index.html

这个例子在Servlet规范-3.6请求路径元素中有更详细的描述。

Tuckey's UrlRewriteFilter的文档中包含了一个更完整的示例,展示了在ServletRequest中如何组成URL。
    // http://hostname.com:80/mywebapp/servlet/MyServlet/a/b;c=123?d=789
    public static String getUrl(HttpServletRequest req) {
        String scheme = req.getScheme();             // http
        String serverName = req.getServerName();     // hostname.com
        int serverPort = req.getServerPort();        // 80
        String contextPath = req.getContextPath();   // /mywebapp
        String servletPath = req.getServletPath();   // /servlet/MyServlet
        String pathInfo = req.getPathInfo();         // /a/b;c=123
        String queryString = req.getQueryString();   // d=789

        // Reconstruct original requesting URL
        String url = scheme + "://" + serverName + ":" + serverPort + contextPath + servletPath;
        if (pathInfo != null) {
            url += pathInfo;
        }
        if (queryString != null) {
            url += "?" + queryString;
        }
        return url;
    }

-1
也许你可以使用split方法来消除'/myapp',例如:
string[] uris=request.getRequestURI().split("/");
string uri="/"+uri[1]+"/"+uris[2];

3
如果我将应用程序部署为root并且其基本URL变为http://localhost:8080/,那么这会导致问题。在这种情况下,request.getRequestURI()将返回“/secure/user”,您的split方法将在此处引起问题。代码不应该依赖于部署。 - craftsman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接