Sprawdzanie poprawności adresu URL w Javie


104

Chciałem wiedzieć, czy w Javie są jakieś standardowe interfejsy API do walidacji podanego adresu URL? Chcę sprawdzić, czy ciąg adresu URL jest poprawny, tj. Podany protokół jest prawidłowy, a następnie sprawdzić, czy można nawiązać połączenie.

Próbowałem użyć HttpURLConnection, podając adres URL i łącząc się z nim. Pierwsza część mojego wymagania wydaje się być spełniona, ale kiedy próbuję wykonać HttpURLConnection.connect (), generowany jest wyjątek „java.net.ConnectException: odmowa połączenia”.

Czy może to być spowodowane ustawieniami proxy? Próbowałem ustawić właściwości systemu dla serwera proxy, ale bez powodzenia.

Daj mi znać, co robię źle.


2
Wydaje się, że są tu 2 pytania; Sprawdzanie poprawności adresu URL i znajdowanie przyczyny wyjątku ConnectException
Ben James

Ponieważ jest to pierwsze trafienie w wyszukiwarce Google java url validator, rzeczywiście istnieją tutaj pytania, jak sprawdzić poprawność adresu URL (patrząc na ciąg znaków) i jak sprawdzić, czy adres URL jest osiągalny (na przykład przez połączenie http).
vikingsteve

Odpowiedzi:


158

Z korzyścią dla społeczności, ponieważ ten wątek jest na górze w Google podczas wyszukiwania hasła
url validator java


Łapanie wyjątków jest kosztowne i należy go unikać, gdy jest to możliwe. Jeśli chcesz tylko sprawdzić, czy Twój ciąg jest prawidłowym adresem URL, możesz użyć klasy UrlValidator z projektu Apache Commons Validator .

Na przykład:

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

38
Ta klasa URLValidator jest oznaczona jako przestarzała. Zalecany URLValidator znajduje się w pakiecie procedur: commons.apache.org/validator/apidocs/org/apache/commons/ ...
Spektr

6
@Spektr Naprawiłem link. Dzięki.
Yonatan

18
Nie widzę, jak to jest standardowe API
b1nary.atr0phy

2
UrlValidator ma własny zestaw znanych problemów. Czy istnieje inna biblioteka, która jest bardziej aktywnie utrzymywana?
Alex Averbuch

9
@AlexAverbuch: czy możesz opisać problemy związane z UrlValidator? Nie warto po prostu powiedzieć, że istnieją, ale nie powiedzieć, czym one są.
cdmckay

33

Musisz utworzyć zarówno URLobiekt, jak i URLConnectionobiekt. Poniższy kod sprawdzi zarówno format adresu URL, jak i możliwość nawiązania połączenia:

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
}

Zauważ, że istnieje wiele sposobów sprawdzania nieprawidłowych adresów URL / problemów. Na przykład, jeśli będziesz używać swojego adresu URL jako adresu URL new HttpGet(url), możesz złapać IllegalArgumentException HttpGet(...)wyrzuty, jeśli jest zniekształcony adres URL. I HttpResponsebędzie rzucać w ciebie różnymi rzeczami, jeśli wystąpi problem z uzyskaniem danych.
Peter Ajtai

2
Połączenie sprawdza tylko dostępność hosta. Nie ma nic wspólnego z poprawnością adresu URL.
Andrey Rodionov

2
Wyjątek MalformedURLException nie jest bezpieczną strategią testowania prawidłowej formy adresu URL. Ta odpowiedź jest myląca.
Martin

1
@ Martin: czy możesz wyjaśnić, dlaczego nie jest to bezpieczne?
Jeroen Vannevel

28
To jest bardzo, bardzo drogie. openConnection / connect faktycznie spróbuje połączyć się z zasobem http. To musi być jeden z najdroższych sposobów weryfikacji adresu URL, jakie kiedykolwiek widziałem.
Glenn Bech

33

W rzeczywistości java.net.URLklasa nie jest dobrym sposobem sprawdzania poprawności adresów URL. nieMalformedURLException jest generowany w przypadku wszystkich źle sformułowanych adresów URL podczas tworzenia. Łowienie na nie sprawdza adres URL albo tylko powiedzieć, pogoda też nie można nawiązać połączenia.IOExceptionjava.net.URL#openConnection().connect()

Rozważ ten fragment kodu:

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

.. który nie powoduje żadnych wyjątków.

Zalecam użycie jakiegoś API walidacyjnego zaimplementowanego przy użyciu gramatyki bezkontekstowej lub w bardzo uproszczonej walidacji po prostu użyj wyrażeń regularnych. Jednak potrzebuję kogoś, kto zasugeruje lepsze lub standardowe API do tego, dopiero niedawno zacząłem go szukać samodzielnie.

Uwaga Sugerowano, że URL#toURI()w połączeniu z obsługą wyjątku java.net. URISyntaxExceptionmoże ułatwić walidację adresów URL. Jednak ta metoda wychwytuje tylko jeden z bardzo prostych przypadków powyżej.

Wniosek jest taki, że nie ma standardowego parsera adresów URL w języku Java do sprawdzania poprawności adresów URL.


Czy znalazłeś rozwiązanie tego problemu?
kidd0

@ bi0s.kidd0 Istnieje kilka bibliotek, z których można korzystać, ale zdecydowaliśmy się wprowadzić własne. Nie jest kompletny, ale może przeanalizować to, co nas interesuje, w tym adresy URL zawierające domeny lub adresy IP (zarówno v4, jak i v6). github.com/jajja/arachne
Martin

15

Używając tylko standardowego interfejsu API, przekaż ciąg do URLobiektu, a następnie przekonwertuj go na URIobiekt. Pozwoli to dokładnie określić poprawność adresu URL zgodnie ze standardem RFC2396.

Przykład:

public boolean isValidURL(String url) {

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

    return true;
}

5
Zauważ, że ten schemat sprawdzania poprawności string-> url-> uri zgłasza, że ​​te przypadki testowe są prawidłowe: "http: //.com" " com ." "ftp: // :::: @ example.com" "http: /test.com" "http: test.com" "http: /:" Tak więc chociaż jest to standardowy interfejs API, stosowane przez niego reguły walidacji mogą nie być czego się oczekuje.
DaveK

10

Użyj android.webkit.URLUtilna Androida:

URLUtil.isValidUrl(URL_STRING);

Uwaga: to tylko sprawdzenie początkowego schematu adresu URL, a nie całego adresu URL.


2
Tylko jeśli pracujesz oczywiście na aplikacji na Androida.
miva2

8

Istnieje sposób na przeprowadzenie weryfikacji adresów URL w ścisłej zgodności ze standardami w Javie bez uciekania się do bibliotek innych firm:

boolean isValidURL(String url) {
  try {
    new URI(url).parseServerAuthority();
    return true;
  } catch (URISyntaxException e) {
    return false;
  }
}

Konstruktor URIsprawdza, czy urljest prawidłowym identyfikatorem URI, a wywołanie parseServerAuthorityzapewnia, że ​​jest to adres URL (bezwzględny lub względny), a nie URN.


Zgłaszany jest wyjątek „Jeśli składnik uprawnień tego identyfikatora URI jest zdefiniowany, ale nie można go przeanalizować jako uprawnienia opartego na serwerze zgodnie z RFC 2396”. Chociaż jest to znacznie lepsze niż większość innych propozycji, nie może zweryfikować adresu URL.
Martin

@Martin, zapomniałeś o walidacji w konstruktorze. Jak napisałem, połączenie URIwywołania konstruktora i parseServerAuthoritywywołania weryfikuje adres URL, a nie parseServerAuthoritysamodzielnie.
dened

1
Na tej stronie możesz znaleźć przykłady, które zostały nieprawidłowo potwierdzone przez Twoją sugestię. Zapoznaj się z dokumentacją, a jeśli nie jest przeznaczona do zamierzonego użytku, nie promuj jej wykorzystywania.
Martin

@ Martin, czy możesz być bardziej szczegółowy? Które przykłady według Ciebie są nieprawidłowo sprawdzane tą metodą?
dened

1
@Asu tak. Druga ://znajduje się po hoście, :wprowadza numer portu, który zgodnie ze składnią może być pusty. //jest częścią ścieżki z pustym segmentem, co również jest poprawne. Jeśli wpiszesz ten adres w przeglądarce, spróbuje go otworzyć (ale najprawdopodobniej nie znajdzie serwera o nazwie https;)).
dened

2

Ważne jest, aby zaznaczyć, że obiekt URL obsługuje zarówno walidację, jak i połączenie. Wtedy autoryzowane są tylko protokoły, dla których procedura obsługi została udostępniona w sun.net.www.protocol ( plik , ftp , gopher , http , https , jar , mailto , netdoc ). Na przykład spróbuj utworzyć nowy adres URL za pomocą protokołu ldap :

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

Otrzymasz java.net.MalformedURLException: unknown protocol: ldap.

Musisz zaimplementować własny program obsługi i zarejestrować go za pośrednictwem URL.setURLStreamHandlerFactory(). Dość przesada, jeśli chcesz tylko sprawdzić składnię adresu URL, regexp wydaje się prostszym rozwiązaniem.


1

Czy na pewno używasz prawidłowego serwera proxy jako właściwości systemu?

Ponadto, jeśli używasz wersji 1.5 lub 1.6, możesz przekazać instancję java.net.Proxy do metody openConnection (). To jest bardziej eleganckie imo:

//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);

Dlaczego miałoby to być eleganckie, a nawet poprawne? Używa drogich zasobów, gdy działa, i nie działa, jeśli poprawny adres URL nie jest dostępny do połączenia podczas testowania.
Martin,

0

Myślę, że najlepszą odpowiedzią jest użytkownik @ b1nary.atr0phy. W jakiś sposób radzę połączyć metodę z odpowiedzi b1nay.atr0phy z wyrażeniem regularnym, aby uwzględnić wszystkie możliwe przypadki.

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;  

    }

1
Jest kilka problemów z tym wyrażeniem regularnym: 1. Adresy URL bez prefiksu są nieprawidłowe (np. „Stackoverflow.com”), dotyczy to również adresów URL z dwoma sufiksami, jeśli brakuje w nich prefiksu (np. „Amazon.co.uk ”). 2. Adresy IP są zawsze nieprawidłowe (np. „ 127.0.0.1” ), bez względu na to, czy używają prefiksu, czy nie. Sugerowałbym użycie "((http|https|ftp)://)?((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"( źródło ). Jedynym minusem tego wyrażenia regularnego jest to, że np. „127.0..0.1” i „127.0” są prawidłowe.
Neph

-2

Dzięki. Otwarcie połączenia URL przez przekazanie proxy zgodnie z sugestią NickDK działa dobrze.

//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);

Właściwości systemu jednak nie działają, jak wspomniałem wcześniej.

Dzięki jeszcze raz.

Pozdrawiam, Keya

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.