如何在Java 1.6中验证URL是否有效?

4

我的应用程序处理用户手动输入的URL。我发现一些格式不正确的URL(比如'http:/not-valid')在打开连接时会导致NullPointerException异常。从这个Java bug报告中了解到,该问题已知并且不会被修复。建议使用java.net.URI,它更符合RFC 2396标准。

问题是:如何使用URI来解决这个问题?我唯一能做的就是使用它来解析字符串并生成URL。我准备了以下程序:

import java.net.*;

public class Test
{
    public static void main(String[] args)
    {
       try {
           URI uri = URI.create(args[0]);
           Object o = uri.toURL().getContent(); // try to get content
       }
       catch(Throwable e) {
           e.printStackTrace();
       }
    }
}

这里是我的测试结果(使用Java 1.6.0_20),与java.net.URL获取的结果差别不大:

sh-3.2$ java Test url-not-valid
java.lang.IllegalArgumentException: URI不是绝对URI
        at java.net.URI.toURL(URI.java:1080)
        at Test.main(Test.java:9)
sh-3.2$ java Test http:/url-not-valid
java.lang.NullPointerException
        at sun.net.www.ParseUtil.toURI(ParseUtil.java:261)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:795)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:726)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1049)
        at java.net.URLConnection.getContent(URLConnection.java:688)
        at java.net.URL.getContent(URL.java:1024)
        at Test.main(Test.java:9)
sh-3.2$ java Test http:///url-not-valid
java.lang.IllegalArgumentException: 协议 = http 主机 = null
        at sun.net.spi.DefaultProxySelector.select(DefaultProxySelector.java:151)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:796)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:726)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1049)
        at java.net.URLConnection.getContent(URLConnection.java:688)
        at java.net.URL.getContent(URL.java:1024)
        at Test.main(Test.java:9)
sh-3.2$ java Test http:////url-not-valid
java.lang.NullPointerException
        at sun.net.www.ParseUtil.toURI(ParseUtil.java:261)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:795)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:726)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1049)
        at java.net.URLConnection.getContent(URLConnection.java:688)
        at java.net.URL.getContent(URL.java:1024)
        at Test.main(Test.java:9)

它应该是Java 6 - Java 1.6不再被官方使用。 - Péter Török
@Pete Kirkham,另一方面,我在Sun的Java网站上找不到任何“JDK 1.6”。 - Péter Török
JDK版本为1.6,Java平台版本为6。虽然有些令人困惑,但事实就是如此。 - skaffman
@skaffman,实际上,JDK在文档中的版本号是6,但其内部版本仍然是1.6.x。确实令人困惑 :-/ - Péter Török
可能是Java中验证URL的重复问题 - JasonB
显示剩余3条评论
3个回答

1
如果我使用错误报告中的格式不正确的URI类型运行您的代码,则会抛出URISyntaxException。因此,建议的修复程序可以修复报告的错误。
$ java -cp bin UriTest http:\\\\www.google.com\\
java.lang.IllegalArgumentException
    at java.net.URI.create(URI.java:842)
    at UriTest.main(UriTest.java:8)
Caused by: java.net.URISyntaxException: Illegal character in opaque part at index 5: http:\\www.google.com\
    at java.net.URI$Parser.fail(URI.java:2809)
    at java.net.URI$Parser.checkChars(URI.java:2982)
    at java.net.URI$Parser.parse(URI.java:3019)
    at java.net.URI.(URI.java:578)
    at java.net.URI.create(URI.java:840)
您的格式不正确的URI类型不同,似乎不是语法错误。
相反,捕获空指针异常并使用适当的消息进行恢复。
您可以尝试友好地检查URI是否以单个斜杠“http:/”开头,并向用户提供建议,或者您可以检查URL的主机名是否非空:
import java.net.*;

public class UriTest
{
    public static void main ( String[] args )
    {
        try {
            URI uri = URI.create ( args[0] );

            // avoid null pointer exception
            if ( uri.getHost() == null )
                throw new MalformedURLException ( "no hostname" );

            URL url = uri.toURL();
            URLConnection s = url.openConnection();

            s.getInputStream();
        } catch ( Throwable e ) {
            e.printStackTrace();
        }
    }
}

我不想手动检查每个可能的URL问题,因为我的理解是这正是URL / URI应该为我做的(如果我在这里错了,这可以是我应该做什么的提示)。在这里捕获运行时异常是我考虑应用的丑陋hack,但总的来说,这个想法很糟糕 - 它可能会隐藏在连接期间发生的其他致命错误。我相信应该存在更好的解决方案。 - Bartłomiej Kalinowski
@Bartłomiej Kalinowski,URI 正在 为您进行检查 - 如果主机名为空(当URL.getHost().equals("")时,URI.getHost()==null),则会抛出NPE以表示您正在连接到空主机。 NPE可能会隐藏其他错误,但是在这些情况下,您希望做什么呢?无论错误是什么,您可能需要要求用户进行更正/重试/中止,因此区别并不是非常重要 - 可能IO异常和其他异常之间的区别很重要 - 您可以在IO错误时自动重试,但不能在其他错误上重试。 - Pete Kirkham
我需要检测URL是否错误,并将此问题与所有其他问题(如I / O错误等)区分开来。应用程序不与用户交互,因此我无法要求用户重试/更正,而且我可以预期URL是错误的(即使之前已经验证过)。 - Bartłomiej Kalinowski

1

我会检查一下。我的感觉是:如果使用外部验证器是正确使用java.net.URL/URI的唯一方法,那么我会说我所描述的行为是一个bug,使得这些类变得有点不可用 - 这对于标准类来说真的很奇怪。也许我只是不知道一些重要的细节/使用前提条件? - Bartłomiej Kalinowski

0
请注意,即使使用其他答案中提出的方法,您也无法正确进行验证,因为java.net.URI遵循已明显过时的RFC 2396。通过使用java.net.URI,您将获得对于今天所有Web浏览器都是有效的URL的异常。
为了解决这些问题,我编写了一个用于Java URL解析的库:galimatias。它以与Web浏览器相同的方式执行URL解析(遵循WHATWG URL规范)。
在您的情况下,您可以编写:
try {
    URL url = io.mola.galimatias.URL.parse(url).toJavaURL();
} catch (GalimatiasParseException e) {
    // If this exception is thrown, the given URL contains a unrecoverable error. That is, it's completely invalid.
}

作为一个好的副作用,使用java.net.URI无法得到的很多净化处理也会自动完成。例如,http:/example.com将被正确地解析为http://example.com/

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