在Java中验证URL

119

我想知道在Java中是否有任何标准API可以验证给定的URL?

我想检查URL字符串是否正确,即所提供的协议是否有效,并检查是否可以建立连接。

我尝试使用HttpURLConnection,提供URL并连接到它。 我的第一个要求似乎已经满足了,但是当我尝试执行HttpURLConnection.connect()时,会抛出“java.net.ConnectException:连接被拒绝”的异常。

这可能是代理设置导致的吗?我尝试设置代理的系统属性,但没有成功。

请告诉我我做错了什么。


2
这里似乎有两个问题:URL验证和查找ConnectException的原因。 - Ben James
由于这是“java url validator”的第一个谷歌搜索结果,因此确实有两个问题需要解决:如何验证URL(从字符串中查看),以及如何检查URL是否可访问(例如通过HTTP连接)。 - vikingsteve
11个回答

173

为了造福社区,因为这个线程在搜索"url validator java"时是谷歌排名第一的


捕获异常是昂贵的,应该尽可能避免。如果你只想验证你的字符串是否为有效的 URL,你可以使用 UrlValidator 类来自 Apache Commons Validator 项目。

例如:

String[] schemes = {"http","https"}; // DEFAULT schemes = "http", "https", "ftp"
UrlValidator urlValidator = new UrlValidator(schemes);
if (urlValidator.isValid("ftp://foo.bar.com/")) {
   System.out.println("URL is valid");
} else {
   System.out.println("URL is invalid");
}

42
URLValidator类已被标记为弃用。推荐使用的URLValidator位于routines包中:http://commons.apache.org/validator/apidocs/org/apache/commons/validator/routines/UrlValidator.html - Spektr
26
我不明白这是如何符合标准 API的。 - arkon
6
UrlValidator 有自己的已知问题。是否有其他库被更积极地维护着? - Alex Averbuch
11
@AlexAverbuch:您可以概述一下UrlValidator存在哪些问题吗?仅仅说它们存在并没有说明具体问题,这并没有太大的帮助。 - cdmckay
3
我们使用安全扫描软件来识别第三方库中的安全漏洞,不幸的是commons-validator包含了commons-beanutils,该组件被标记为红色(安全漏洞)。是否有另一个(更轻量级的)库/API可供使用? - vikingsteve
显示剩余6条评论

41

java.net.URL类实际上并不是一种好的验证URL的方式。在构建过程中,并不会对所有格式错误的URL引发MalformedURLException异常。在java.net.URL#openConnection().connect()上捕获IOException也不能验证URL,只能告诉连接是否可以建立。

考虑以下代码:

    try {
        new URL("http://.com");
        new URL("http://com.");
        new URL("http:// ");
        new URL("ftp://::::@example.com");
    } catch (MalformedURLException malformedURLException) {
        malformedURLException.printStackTrace();
    }

......不会引发任何异常。

我建议使用一些基于上下文无关语法实现的验证API,或者在非常简化的验证中只使用正则表达式。然而,我需要有人提出一个更好或标准的API,因为我自己最近才开始搜索。

注意 有人建议结合处理异常java.net.URISyntaxExceptionURL#toURI()可以方便地验证URL。然而,该方法仅能捕获以上很简单的一种情况。

结论是,没有标准的Java URL解析器用于验证URL。


你找到这个问题的解决方案了吗? - kidd0
@bi0s.kidd0 有几个库可以使用,但我们决定自己编写。它还不完整,但可以解析我们感兴趣的内容,包括包含域名或IP(v4和v6)的URL。https://github.com/jajja/arachne - Martin

33

您需要创建一个URL对象和一个URLConnection对象。以下代码将测试URL的格式以及是否可以建立连接:

try {
    URL url = new URL("http://www.yoursite.com/");
    URLConnection conn = url.openConnection();
    conn.connect();
} catch (MalformedURLException e) {
    // the URL is not in a valid form
} catch (IOException e) {
    // the connection couldn't be established
}

2
连接仅验证主机的可用性,与URL的有效性无关。 - dernasherbrezon
2
MalformedURLException不是测试URL有效表单的安全策略。这个答案是误导性的。 - Martin
1
@Martin:你能详细说明为什么它不安全吗? - Jeroen Vannevel
39
这非常非常昂贵。openConnection/connect会尝试连接到http资源。这可能是我见过的验证URL最昂贵的方式之一。 - Glenn Bech
这需要一个实际的连接... 如果没有连接,URL仍然可以有效... - undefined
显示剩余4条评论

25

仅使用标准API,将字符串传递给URL对象,然后将其转换为URI对象。这将根据RFC2396标准准确确定URL的有效性。

示例:

public boolean isValidURL(String url) {

    try {
        new URL(url).toURI();
    } catch (MalformedURLException | URISyntaxException e) {
        return false;
    }

    return true;
}

13
请注意,这种字符串->网址->URI验证方案报告这些测试用例是有效的:"http://.com" "http://com." "ftp://::::@example.com" "http:/test.com" "http:test.com" "http:/:"所以,虽然这是标准API,但它应用的验证规则可能不符合预期。 - DaveK
@DaveK 你是在说RFC2396规范有问题,还是JAVA URI实现没有遵守RFC2396标准规范? - undefined
@marcolopes 不是的。我是在建议RFC2396允许一系列有效的格式,这可能会让人感到惊讶并且比预期更宽容,所以根据个人的需求,可能需要进行额外的验证步骤。 - undefined
@DaveK 所以,你是在说RFC2396的“标准”是宽容的... - undefined

11

在Java中,有一种严格按照标准执行URL验证的方法,而不需要借助第三方库:

boolean isValidURL(String url) {
  try {
    new URI(url).parseServerAuthority();
    return true;
  } catch (URISyntaxException e) {
    return false;
  }
}
URI 的构造函数会检查 url 是否为有效的 URI,而调用 parseServerAuthority 确保它是一个 URL(绝对或相对),而不是 URN。

1
@Martin,你忘记在构造函数中进行验证了。正如我所写的那样,URI构造函数调用和parseServerAuthority调用的组合会验证URL,而不仅仅是parseServerAuthority - dened
1
您可以在此页面上找到一些由您的建议错误验证的示例。请参考文档,如果它不适用于您的预期用途,请不要推广以利用它。 - Martin
2
@Asu 是的。第二个 :// 出现在主机后面,: 引入端口号,根据语法可以为空。// 是路径的一部分,具有空段,这也是有效的。 如果您在浏览器中输入此地址,它将尝试打开它(但很可能找不到名为 https 的服务器 ;))。 - dened
1
唉..说得好。让端口号在冒号后面为空完全是违反直觉的。 - Asu
那么,URI的Java实现是否遵守RFC2396标准规范? - undefined
显示剩余4条评论

8

在安卓系统上,可以使用 android.webkit.URLUtil 工具:

URLUtil.isValidUrl(URL_STRING);

注意:它只是检查URL的初始方案,而不是整个URL是否有效。

2
当然, 只有在开发安卓应用时才需要这样做。 - miva2
它只检查URL是否以正确的前缀开头:http://,https//,about:等。 - molokoka

1

重要的是指出URL对象处理验证和连接。然后,只有在sun.net.www.protocol中提供了处理程序的协议才被授权(文件,ftp,gopher,http,https,jar,mailto,netdoc)是有效的。例如,尝试使用LDAP协议创建新的URL:

new URL("ldap://myhost:389")

你会得到一个 java.net.MalformedURLException: unknown protocol: ldap

你需要实现自己的处理程序,并通过 URL.setURLStreamHandlerFactory() 注册它。如果你只想验证URL语法,使用正则表达式似乎是一个更简单的解决方案。


0
你确定你正在使用正确的代理作为系统属性吗?
此外,如果您正在使用1.5或1.6,您可以将java.net.Proxy实例传递给openConnection()方法。在我看来,这更优雅:
//Proxy instance, proxy ip = 10.0.0.1 with port 8080
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("10.0.0.1", 8080));
conn = new URL(urlString).openConnection(proxy);

这为什么会优雅或者正确呢?它在工作时使用昂贵的资源,而且当测试时无法连接到正确的URL时,它也无法正常工作。 - Martin

-1

这是我用来验证CDN网址的方法(必须以https开头,但很容易自定义)。这也不允许使用IP地址。

public static final boolean validateURL(String url) {  
    var regex = Pattern.compile("^[https:\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)");
    var matcher = regex.matcher(url);
    return matcher.find();
}

-1

我认为最好的回答来自用户@b1nary.atr0phy。不知何故,我建议将b1nay.atr0phy响应的方法与正则表达式结合起来,以涵盖所有可能的情况。

public static final URL validateURL(String url, Logger logger) {

        URL u = null;
        try {  
            Pattern regex = Pattern.compile("(?i)^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))\\.?)(?::\\d{2,5})?(?:[/?#]\\S*)?$");
            Matcher matcher = regex.matcher(url);
            if(!matcher.find()) {
                throw new URISyntaxException(url, "La url no está formada correctamente.");
            }
            u = new URL(url);  
            u.toURI(); 
        } catch (MalformedURLException e) {  
            logger.error("La url no está formada correctamente.");
        } catch (URISyntaxException e) {  
            logger.error("La url no está formada correctamente.");  
        }  

        return u;  

    }

2
这个正则表达式有几个问题:1. 没有前缀的URL是无效的(例如“stackoverflow.com”),如果它们缺少前缀,则包括具有两个后缀的URL(例如“amazon.co.uk”)。2. IP地址始终无效(例如“ftp://127.0.0.1”),无论它们是否使用前缀。我建议使用`"((http|https|ftp)://)?((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"`([来源](https://dev59.com/87Xna4cB1Zd3GeqPJFUu#57219660))。这个正则表达式唯一的缺点是,例如“127.0..0.1”和“127.0”是有效的。 - Neph

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