Xác thực URL trong Java


103

Tôi muốn biết liệu có bất kỳ API tiêu chuẩn nào trong Java để xác thực một URL nhất định không? Tôi muốn kiểm tra cả hai xem chuỗi URL có đúng hay không, tức là giao thức đã cho có hợp lệ và sau đó để kiểm tra xem kết nối có thể được thiết lập hay không.

Tôi đã thử sử dụng HttpURLConnection, cung cấp URL và kết nối với nó. Phần đầu tiên của yêu cầu của tôi dường như được đáp ứng nhưng khi tôi cố gắng thực hiện HttpURLConnection.connect (), ngoại lệ 'java.net.ConnectException: Connection từ chối' được ném ra.

Điều này có thể là do cài đặt proxy? Tôi đã thử đặt thuộc tính Hệ thống cho proxy nhưng không thành công.

Hãy cho tôi biết những gì tôi đang làm sai.


2
Có vẻ như có 2 câu hỏi ở đây; Xác nhận URL và tìm ra nguyên nhân của một ConnectException
Bến James

Vì đây là lần truy cập đầu tiên của google java url validator, thực sự có nhiều câu hỏi ở đây, làm thế nào để xác thực url (bằng cách xem xét chuỗi) và cách kiểm tra xem url có thể truy cập được hay không (ví dụ: thông qua kết nối http).
vikingsteve

Câu trả lời:


157

Vì lợi ích của cộng đồng, vì chuỗi này đứng đầu trên Google khi tìm kiếm
" trình xác thực url java "


Bắt các trường hợp ngoại lệ là tốn kém và nên tránh khi có thể. Nếu bạn chỉ muốn xác minh Chuỗi của mình là một URL hợp lệ, bạn có thể sử dụng lớp UrlValidator từ dự án Apache Commons Validator .

Ví dụ:

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

37
Lớp URLValidator đó được đánh dấu là không dùng nữa. URLValidator được đề xuất nằm trong gói quy trình: commons.apache.org/validator/apidocs/org/apache/commons/…
Spektr

6
@Spektr Tôi đã sửa liên kết. Cảm ơn.
Yonatan

18
Tôi không hiểu đây là API tiêu chuẩn
b1nary.atr0phy

2
UrlValidator có tập hợp các vấn đề đã biết của riêng nó. Có một thư viện thay thế đang được duy trì tích cực hơn không?
Alex Averbuch

9
@AlexAverbuch: bạn có thể vui lòng phác thảo các vấn đề với UrlValidator không? Sẽ không hữu ích lắm nếu chỉ nói rằng chúng tồn tại nhưng không nói chúng là gì.
cdmckay

33

Bạn cần tạo cả một URLđối tượng và một URLConnectionđối tượng. Đoạn mã sau sẽ kiểm tra cả định dạng của URL và liệu kết nối có thể được thiết lập hay không:

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
}

Lưu ý rằng có nhiều cách để kiểm tra url / sự cố không đúng định dạng. Ví dụ: nếu bạn sẽ sử dụng url của mình cho a new HttpGet(url), thì bạn có thể bắt được các lần IllegalArgumentException HttpGet(...)ném nếu có url không đúng định dạng. Và HttpResponsecũng sẽ ném mọi thứ vào bạn nếu có vấn đề với việc lấy dữ liệu.
Peter Ajtai

2
Kết nối chỉ xác nhận tính khả dụng của máy chủ. Không liên quan gì đến tính hợp lệ của URL.
Andrey Rodionov

2
MalformedURLException không phải là một chiến lược an toàn để kiểm tra dạng URL hợp lệ. Câu trả lời này gây hiểu lầm.
Martin

1
@Martin: bạn có thể giải thích tại sao nó không an toàn không?
Jeroen Vannevel

28
Cái này rất, rất đắt. openConnection / connect thực sự sẽ cố gắng kết nối với tài nguyên http. Đây hẳn là một trong những cách tốn kém nhất mà tôi từng thấy để xác minh một URL.
Glenn Bech

33

Các java.net.URLlớp là trong thực tế không phải ở tất cả một cách tốt để xác nhận URL. MalformedURLExceptionkhông ném vào tất cả các URL bị thay đổi trong quá trình thi. bắt IOExceptiontrênjava.net.URL#openConnection().connect() cũng không xác thực URL, chỉ cho người biết hoặc không thể thiết lập kết nối.

Hãy xem xét đoạn mã này:

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

..mà không ném bất kỳ ngoại lệ nào.

Tôi khuyên bạn nên sử dụng một số API xác thực được triển khai bằng ngữ pháp không có ngữ cảnh hoặc trong quá trình xác thực rất đơn giản, chỉ cần sử dụng biểu thức chính quy. Tuy nhiên, tôi cần ai đó đề xuất một API cao cấp hoặc tiêu chuẩn cho việc này, tôi chỉ mới bắt đầu tự tìm kiếm nó gần đây.

Lưu ý Người ta đã đề xuất rằng URL#toURI()kết hợp với việc xử lý ngoại lệjava.net. URISyntaxException có thể tạo điều kiện cho việc xác thực URL. Tuy nhiên, phương pháp này chỉ bắt được một trong những trường hợp rất đơn giản ở trên.

Kết luận là không có trình phân tích cú pháp URL java tiêu chuẩn để xác thực URL.


Bạn đã tìm ra giải pháp cho vấn đề này chưa ??
kidd

@ bi0s.kidd0 Có một số thư viện có thể được sử dụng, nhưng chúng tôi quyết định sử dụng thư viện của riêng mình. Nó không hoàn chỉnh, nhưng có thể phân tích cú pháp những gì chúng tôi quan tâm, bao gồm các URL chứa miền hoặc IP (cả v4 và v6). github.com/jajja/arachne
Martin

15

Chỉ sử dụng API tiêu chuẩn, chuyển chuỗi cho một URLđối tượng sau đó chuyển đổi nó thành một URIđối tượng. Điều này sẽ xác định chính xác tính hợp lệ của URL theo tiêu chuẩn RFC2396.

Thí dụ:

public boolean isValidURL(String url) {

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

    return true;
}

5
Lưu ý rằng lược đồ xác thực string-> url-> uri báo cáo rằng các trường hợp kiểm tra này là hợp lệ: "http: //.com" " com ." "ftp: // :::: @ example.com" "http: /test.com" "http: test.com" "http: /:" Vì vậy, mặc dù đây là API tiêu chuẩn, các quy tắc xác thực nó áp dụng có thể không những gì người ta mong đợi.
DaveK

10

Sử dụng android.webkit.URLUtiltrên android:

URLUtil.isValidUrl(URL_STRING);

Lưu ý: Nó chỉ kiểm tra lược đồ ban đầu của URL, không phải toàn bộ URL là hợp lệ.


2
Chỉ khi bạn đang làm việc trên một ứng dụng Android.
miva2

8

Có một cách để thực hiện xác thực URL theo đúng tiêu chuẩn trong Java mà không cần dùng đến thư viện của bên thứ ba:

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

Phương thức khởi tạo URIkiểm tra xem đó urllà URI hợp lệ và lệnh gọi để parseServerAuthorityđảm bảo rằng đó là một URL (tuyệt đối hoặc tương đối) chứ không phải URN.


Ngoại lệ được đưa ra "Nếu thành phần quyền hạn của URI này được xác định nhưng không thể phân tích cú pháp thành quyền dựa trên máy chủ theo RFC 2396". Mặc dù điều này tốt hơn nhiều so với hầu hết các đề xuất khác, nhưng nó không thể xác thực URL.
Martin

@Martin, Bạn quên xác thực trong hàm tạo. Như tôi đã viết, sự kết hợp của lệnh URIgọi hàm tạo và lệnh parseServerAuthoritygọi xác thực URL, không phải parseServerAuthoritymột mình.
dened

1
Bạn có thể tìm thấy các ví dụ trên trang này được xác thực không chính xác theo đề xuất của bạn. Tham khảo tài liệu, và nếu nó không được thiết kế cho mục đích sử dụng của bạn, vui lòng không quảng bá để khai thác nó.
Martin

@Martin, Bạn có thể nói rõ hơn được không? Những ví dụ nào theo quan điểm của bạn là không chính xác bằng phương pháp này?
dened

1
@Asu vâng. Thứ hai ://đến sau máy chủ, :giới thiệu số cổng, có thể để trống theo cú pháp. //là một phần của đường dẫn có phân đoạn trống, phân đoạn này cũng hợp lệ. Nếu bạn nhập địa chỉ này vào trình duyệt của mình, nó sẽ cố gắng mở nó (nhưng hầu hết có thể sẽ không tìm thấy máy chủ có tên https;)).
dened

2

Điều quan trọng cần lưu ý là đối tượng URL xử lý cả xác thực và kết nối. Sau đó, chỉ các giao thức mà trình xử lý đã được cung cấp trong sun.net.www.protocol mới được ủy quyền ( tệp , ftp , gopher , http , https , jar , mailto , netdoc ) mới là giao thức hợp lệ. Ví dụ: hãy thử tạo một URL mới bằng giao thức ldap :

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

Bạn sẽ nhận được một java.net.MalformedURLException: unknown protocol: ldap.

Bạn cần triển khai trình xử lý của riêng mình và đăng ký nó thông qua URL.setURLStreamHandlerFactory(). Khá quá mức cần thiết nếu bạn chỉ muốn xác thực cú pháp URL, regexp có vẻ là một giải pháp đơn giản hơn.


1

Bạn có chắc mình đang sử dụng đúng proxy làm thuộc tính hệ thống không?

Ngoài ra, nếu bạn đang sử dụng 1.5 hoặc 1.6, bạn có thể chuyển một cá thể java.net.Proxy vào phương thức openConnection (). Đây là imo thanh lịch hơn:

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

Tại sao điều này sẽ là thanh lịch hoặc thậm chí chính xác? Nó sử dụng tài nguyên đắt tiền khi nó hoạt động và nó không hoạt động vì một URL chính xác không có sẵn để kết nối khi được kiểm tra.
Martin

0

Tôi nghĩ phản hồi tốt nhất là từ người dùng @ b1nary.atr0phy. Bằng cách nào đó, tôi khuyên bạn nên kết hợp phương thức từ phản hồi b1nay.atr0phy với một regex để bao gồm tất cả các trường hợp có thể xảy ra.

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
Có một số vấn đề với regex này: 1. Các URL không có tiền tố là không hợp lệ, (ví dụ: "stackoverflow.com"), điều này cũng bao gồm các URL có hai hậu tố nếu chúng thiếu tiền tố (ví dụ: "amazon.co.uk "). 2. IP luôn không hợp lệ (ví dụ: " 127.0.0.1" ), bất kể chúng có sử dụng tiền tố hay không. Tôi đề nghị sử dụng "((http|https|ftp)://)?((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"( nguồn ). Nhược điểm duy nhất của regex này là ví dụ "127.0..0.1" và "127.0" là hợp lệ.
Neph

-2

Cảm ơn. Mở kết nối URL bằng cách chuyển Proxy theo đề xuất của NickDK hoạt động tốt.

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

Tuy nhiên, thuộc tính hệ thống không hoạt động như tôi đã đề cập trước đó.

Cảm ơn một lần nữa.

Trân trọng, Keya

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.