Java - 在url中替换主机?

时间:2017-10-10 12:55:00

标签: java

在java中,我想用新的Host替换url的Host部分,其中host和url都是作为字符串提供的。

这应该考虑到主机可以有一个端口这一事实,defined in the RFC

因此,例如,给出以下输入

我应该从正确执行此操作的函数

获得以下输出

有没有人知道在网址中Host替换的任何库或例程?

编辑:对于我的用例,我希望我的主机替换匹配java servlet会响应的内容。我通过运行本地java Web服务器尝试了这一点,然后使用curl -H 'Host:superduper.com:80' 'http://localhost:8000/testurl'对其进行了测试,并让该端点只返回来自request.getRequestURL().toString()的url,其中request是HttpServletRequest。它返回了http://superduper.com/testurl,因此它删除了http的默认端口,这也是我所追求的目标。

6 个答案:

答案 0 :(得分:5)

Spring Framework提供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

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

答案 1 :(得分:4)

我很快尝试使用java.net.URIjavax.ws.rs.core.UriBuilderorg.apache.http.client.utils.URIBuilder,但他们似乎都没有想到可能包含端口的主机头,所以他们都需要一些额外的从我所能看到的逻辑来使它正确发生,没有端口被“加倍”"有时,在其他时候没有正确替换。

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

我把这种做法放在一起,你可以test it out online here at 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);
  }
}

答案 2 :(得分:3)

你使用java.net.URI是对的。主机和端口(以及用户/密码,如果存在)统称为URI的权限组件:

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的方案是required to be lowercase,所以虽然上述代码可以说不能完全保留所有原始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();
}

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

我在RawHTTP库中添加了一种执行此操作的方法,因此您可以轻松执行以下操作:

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

此提交中已添加:https://github.com/renatoathaydes/rawhttp/commit/cbe439f2511f7afcb89b5a0338ed9348517b9163#diff-ff0fec3bc023897ae857b07cc3522366

欢迎反馈,即将发布。

答案 5 :(得分:0)

或使用一些正则表达式魔法:

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;
}