获取Servlet中HTTP和HTTPS请求的完整URL和查询字符串

82

我正在编写一段代码,其任务是检索所请求的 URL 或完整路径。我已经编写了以下代码:

HttpServletRequest request;//obtained from other functions
String uri = request.getRequestURI();
if (request.getQueryString() != null)
    uri += "?" + request.getQueryString();

当我浏览 http://google.com?q=abc 时,一切正常。

但是当我浏览 https://google.com 时,出现了问题。此时uri的值为http://google.com:443google.com:443,因此程序仅在使用HTTPS时才能正常运行。

request.getRequestURL().toString()的输出结果相同。

有什么解决办法吗?


3
我认为最有可能的情况是,构建HttpServletRequest的“其他函数”可能存在错误。也许您可以创建一个SSCCE来演示确切的问题?请注意,我的任务仅限于翻译,不包括其他解释或内容。 - parsifal
5个回答

182

按照设计,getRequestURL() 可以返回完整的URL,只是没有查询字符串。

HttpServletRequest中,您可以使用以下方法获取URI的各个部分:

// Example: http://myhost:8080/people?lastname=Fox&age=30

String uri = request.getScheme() + "://" +   // "http" + "://
             request.getServerName() +       // "myhost"
             ":" +                           // ":"
             request.getServerPort() +       // "8080"
             request.getRequestURI() +       // "/people"
             "?" +                           // "?"
             request.getQueryString();       // "lastname=Fox&age=30"
  • .getScheme()将在https://domain请求时返回"https"
  • .getServerName()http(s)://domain上返回domain
  • .getServerPort()将返回端口号。

使用下面的代码片段:

String uri = request.getScheme() + "://" +
             request.getServerName() + 
             ("http".equals(request.getScheme()) && request.getServerPort() == 80 || "https".equals(request.getScheme()) && request.getServerPort() == 443 ? "" : ":" + request.getServerPort() ) +
             request.getRequestURI() +
            (request.getQueryString() != null ? "?" + request.getQueryString() : "");

上面的代码片段将获取完整的URI,如果使用了默认端口,则隐藏端口,并且如果未提供查询字符串,则不会添加"?"和查询字符串。


代理请求

请注意,如果您的请求通过代理传递,则需要查看X-Forwarded-Proto头,因为方案可能会被更改:

request.getHeader("X-Forwarded-Proto")

同时,X-Forwarded-For 是一个常见的头部信息,在代理服务器中显示的是原始请求的IP地址而不是代理服务器的IP地址。

request.getHeader("X-Forwarded-For")

如果您负责代理/负载均衡器的配置,则需要确保在转发时设置这些标头。


根据答案中的表达式,uri 的值是多少? - acdcjunior
1
@ErBnAcharya,你获取到 google.com:443 两次 真的很奇怪。此外,我看到的是 http 而不是 https... - informatik01
1
我赞同@informatik01的看法,这很奇怪。你可以尝试这样做:逐个打印每个部分,每个部分单独一行(一个是getScheme(),另一个是getServerName()),结果是什么?这将给你一个关于问题所在的提示。 - acdcjunior
嗨!我知道已经很久了,但是你设法让它工作了吗? - acdcjunior
是否存在一种方法也获取URI片段? - agad
显示剩余10条评论

10

使用方法:

String Uri = request.getRequestURL()+"?"+request.getQueryString();

请注意,getRequestURL在错误页面上无法正常工作。因此,如果您有一个404.jsp并且需要进行重定向,则需要添加导入:<%@ page import="org.apache.catalina.util.RequestUtil" %>并使用String requestedLocation = RequestUtil.filter((String) request.getAttribute("javax.servlet.error.request_uri")); - Nux

4
当您在服务器端尝试构建URL时,HTTPS请求变成了HTTP,这表明您可能有一个代理/负载均衡器(例如nginxpound等)在前面卸载SSL加密并以纯HTTP转发到后端服务。
如果是这种情况,请检查以下内容:
  1. 检查代理是否已正确设置以正确转发标头(例如HostX-forwarded-protoX-forwarded-for等)。
  2. 检查您的服务容器(例如Tomcat)是否设置为识别前面的代理。例如,Tomcat需要将secure="true" scheme="https" proxyPort="443"属性添加到其Connector中。
  3. 检查您的代码或服务容器是否正确处理标头。例如,当您向Engine添加RemoteIpValve时,Tomcat会自动替换schemeremoteAddr等值。 (请参见配置指南JavaDoc),因此您无需在代码中手动处理这些标头。
不正确的代理标头值可能会导致request.getRequestURI()request.getRequestURL()尝试构建源URL时输出不正确。

这是问题的解决方案。扩展此内容,请阅读 server.xml 连接器文档!(https://tomcat.apache.org/tomcat-8.0-doc/config/http.html) - peterh

2

我知道这是一个Java问题,但如果你使用Kotlin,你可以很好地做到这一点:

val uri = request.run {
    if (queryString.isNullOrBlank()) requestURI else "$requestURI?$queryString"
}

0

对于Spring用户而言,ServletUriComponentsBuilder 可以处理这个问题。

// autowire HttpServletRequest to use here
String fullURL = ServletUriComponentsBuilder.fromRequest(request).build().toString();
// or use current request
String fullURL = ServletUriComponentsBuilder.fromCurrentRequest().build().toString();

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