Java - 替换网址中的主机?

问题描述 投票:0回答:7

在java中,我想用新的

Host
替换url的
Host
部分,其中主机和url都作为字符串提供。

这应该考虑到主机中可能有一个端口的事实,如 RFC 中定义的

例如,给出以下输入

    网址:
  • http://localhost/me/out?it=5
  • 主机:我的服务器:20000
我应该从正确执行此操作的函数中获得以下输出

  • http://myserver:20000/me/out?it=5
有人知道有哪些库或例程可以正确替换 url 中的

Host

 吗?

编辑:对于我的用例,我希望我的主机替换与 java servlet 的响应相匹配。我通过运行本地 java Web 服务器来尝试这一点,然后使用

curl -H 'Host:superduper.com:80' 'http://localhost:8000/testurl'

 进行测试,并让该端点简单地从 
request.getRequestURL().toString()
 返回 url,其中请求是 
HttpServletRequest
。它返回了
http://superduper.com/testurl
,所以它删除了http的默认端口,所以这也是我正在努力的目标。

java
7个回答
24
投票
Spring 框架提供了

UriComponentsBuilder

。你可以这样使用它:

import org.springframework.web.util.UriComponentsBuilder; String initialUri = "http://localhost/me/out?it=5"; UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(initialUri); String modifiedUri = builder.host("myserver").port("20000").toUriString(); System.out.println(modifiedUri); // ==> http://myserver:20000/me/out?it=5

这里您需要在单独的调用中提供主机名和端口以获得正确的编码。


19
投票
您使用 java.net.URI 是正确的。 主机和端口(以及用户/密码,如果存在)统称为 URI 的

authority 组件:

public static String replaceHostInUrl(String originalURL, String newAuthority) throws URISyntaxException { URI uri = new URI(originalURL); uri = new URI(uri.getScheme().toLowerCase(Locale.US), newAuthority, uri.getPath(), uri.getQuery(), uri.getFragment()); return uri.toString(); }
(URI 的方案

要求为小写,因此,虽然上面的代码可以说不能完美保留原始 URL 的所有非权限部分,但大写方案实际上从一开始就不是合法的。并且,当然,它不会影响 URL 连接的功能。)

请注意,您的一些测试是错误的。 例如:

assertEquals("https://super/me/out?it=5", replaceHostInUrl("https://www.test.com:4300/me/out?it=5","super:443")); assertEquals("http://super/me/out?it=5", replaceHostInUrl("http://www.test.com:4300/me/out?it=5","super:80"));
虽然 

https://super/me/out?it=5

 在功能上与 
https://super:443/me/out?it=5
 相同(因为 https 的默认端口是 443),但如果您在 URI 中指定显式端口,则 URI 会在其权限中指定一个端口,这就是它应该保持的方式。 

更新:

如果您想要删除显式但不必要的端口号,可以使用 URL.getDefaultPort() 来检查它:

public static String replaceHostInUrl(String originalURL, String newAuthority) throws URISyntaxException, MalformedURLException { URI uri = new URI(originalURL); uri = new URI(uri.getScheme().toLowerCase(Locale.US), newAuthority, uri.getPath(), uri.getQuery(), uri.getFragment()); int port = uri.getPort(); if (port > 0 && port == uri.toURL().getDefaultPort()) { uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), -1, uri.getPath(), uri.getQuery(), uri.getFragment()); } return uri.toString(); }
    

4
投票
我很快尝试使用

java.net.URI

javax.ws.rs.core.UriBuilder
org.apache.http.client.utils.URIBuilder
,但它们似乎都不明白主机标头可能包含端口的想法,因此它们都需要一些我能看到的额外逻辑来制作它正确发生,端口有时不会“加倍”,有时也不会正确更换。

由于

java.net.URL

 不需要任何额外的库,所以我使用了它。我确实知道,如果我在某个地方使用 
URL.equals
,这可能是一个问题,因为它可能会进行 DNS 查找,但我不是,所以我认为这很好,因为这涵盖了我的用例,如伪单元测试所示.

我整理了这种方法,您可以

在 repl.it 在线测试它

import java.net.URL; import java.net.MalformedURLException; class Main { public static void main(String[] args) { testReplaceHostInUrl(); } public static void testReplaceHostInUrl() { assertEquals("http://myserver:20000/me/out?it=5", replaceHostInUrl("http://localhost/me/out?it=5","myserver:20000")); assertEquals("http://myserver:20000/me/out?it=5", replaceHostInUrl("http://localhost:19000/me/out?it=5","myserver:20000")); assertEquals("http://super/me/out?it=5", replaceHostInUrl("http://localhost:19000/me/out?it=5","super")); assertEquals("http://super/me/out?it=5", replaceHostInUrl("http://www.test.com/me/out?it=5","super")); assertEquals("https://myserver:20000/me/out?it=5", replaceHostInUrl("https://localhost/me/out?it=5","myserver:20000")); assertEquals("https://myserver:20000/me/out?it=5", replaceHostInUrl("https://localhost:19000/me/out?it=5","myserver:20000")); assertEquals("https://super/me/out?it=5", replaceHostInUrl("https://www.test.com/me/out?it=5","super")); assertEquals("https://super/me/out?it=5", replaceHostInUrl("https://www.test.com:4300/me/out?it=5","super")); assertEquals("https://super/me/out?it=5", replaceHostInUrl("https://www.test.com:4300/me/out?it=5","super:443")); assertEquals("http://super/me/out?it=5", replaceHostInUrl("http://www.test.com:4300/me/out?it=5","super:80")); assertEquals("http://super:8080/me/out?it=5", replaceHostInUrl("http://www.test.com:80/me/out?it=5","super:8080")); assertEquals("http://super/me/out?it=5&test=5", replaceHostInUrl("http://www.test.com:80/me/out?it=5&test=5","super:80")); assertEquals("https://super:80/me/out?it=5&test=5", replaceHostInUrl("https://www.test.com:80/me/out?it=5&test=5","super:80")); assertEquals("https://super/me/out?it=5&test=5", replaceHostInUrl("https://www.test.com:80/me/out?it=5&test=5","super:443")); assertEquals("http://super:443/me/out?it=5&test=5", replaceHostInUrl("http://www.test.com:443/me/out?it=5&test=5","super:443")); assertEquals("http://super:443/me/out?it=5&test=5", replaceHostInUrl("HTTP://www.test.com:443/me/out?it=5&test=5","super:443")); assertEquals("http://SUPERDUPER:443/ME/OUT?IT=5&TEST=5", replaceHostInUrl("HTTP://WWW.TEST.COM:443/ME/OUT?IT=5&TEST=5","SUPERDUPER:443")); assertEquals("https://SUPERDUPER:23/ME/OUT?IT=5&TEST=5", replaceHostInUrl("HTTPS://WWW.TEST.COM:22/ME/OUT?IT=5&TEST=5","SUPERDUPER:23")); assertEquals(null, replaceHostInUrl(null, null)); } public static String replaceHostInUrl(String url, String newHost) { if (url == null || newHost == null) { return url; } try { URL originalURL = new URL(url); boolean hostHasPort = newHost.indexOf(":") != -1; int newPort = originalURL.getPort(); if (hostHasPort) { URL hostURL = new URL("http://" + newHost); newHost = hostURL.getHost(); newPort = hostURL.getPort(); } else { newPort = -1; } // Use implicit port if it's a default port boolean isHttps = originalURL.getProtocol().equals("https"); boolean useDefaultPort = (newPort == 443 && isHttps) || (newPort == 80 && !isHttps); newPort = useDefaultPort ? -1 : newPort; URL newURL = new URL(originalURL.getProtocol(), newHost, newPort, originalURL.getFile()); String result = newURL.toString(); return result; } catch (MalformedURLException e) { throw new RuntimeException("Couldnt replace host in url, originalUrl=" + url + ", newHost=" + newHost); } } public static void assertEquals(String expected, String actual) { if (expected == null && actual == null) { System.out.println("TEST PASSED, expected:" + expected + ", actual:" + actual); return; } if (! expected.equals(actual)) throw new RuntimeException("Not equal! expected:" + expected + ", actual:" + actual); System.out.println("TEST PASSED, expected:" + expected + ", actual:" + actual); } }
    

3
投票
我意识到这是一个很老的问题;但发布一个更简单的解决方案以防其他人需要它。

String newUrl = new URIBuilder(URI.create(originalURL)).setHost(newHost).build().toString();
    

1
投票
我在

RawHTTP 库中添加了一个方法来执行此操作,因此您可以简单地执行此操作:

URI uri = RawHttp.replaceHost(oldUri, "new-host");

在此提交中添加:

https://github.com/renatoahaydes/rawhttp/commit/cbe439f2511f7afcb89b5a0338ed9348517b9163#diff-ff0fec3bc023897ae857b07cc3522366

欢迎反馈,很快就会发布。


0
投票
对于使用 Uri 类的 Android 用户来说,另一个答案是,您可以使用

buildUpon()

 基于现有 Uri 创建一个构建器,然后设置 
authority
(涵盖主机和端口)

Uri myUri = Uri.parse("http://localhost/me/out?it=5"); myUri = myUri.buildUpon().authority("myserver:20000").build();
    

-1
投票
或者使用一些正则表达式魔法:

public static String replaceHostInUrl(String url, String newHost) { if (url == null || newHost == null) { return null; } String s = url.replaceFirst("(?i)(?<=(https?)://)(www.)?\\w*(.com)?(:\\d*)?", newHost); if (s.contains("http://")) { s = s.replaceFirst(":80(?=/)", ""); } else if (s.contains("https://")) { s = s.replaceFirst(":443(?=/)", ""); } Matcher m = Pattern.compile("HTTPS?").matcher(s); if (m.find()) { s = s.replaceFirst(m.group(), m.group().toLowerCase()); } return s; }
    
© www.soinside.com 2019 - 2024. All rights reserved.