Validando URL em Java

103

Eu queria saber se existe alguma API padrão em Java para validar um determinado URL. Quero verificar se a string do URL está correta, ou seja, se o protocolo fornecido é válido, e se uma conexão pode ser estabelecida.

Tentei usar HttpURLConnection, fornecendo a URL e conectando-se a ela. A primeira parte do meu requisito parece ter sido cumprida, mas quando tento executar HttpURLConnection.connect (), a exceção 'java.net.ConnectException: Conexão recusada' é lançada.

Isso pode ser por causa das configurações de proxy? Tentei definir as propriedades do sistema para proxy, mas sem sucesso.

Deixe-me saber o que estou fazendo de errado.

Keya
fonte
2
Parece haver 2 perguntas aqui; Validação de URL e descoberta da causa de uma ConnectException
Ben James,
Uma vez que este é o primeiro hit do Google para java url validator, há de fato algumas questões aqui, como validar o url (olhando para a string) e como verificar se o url está acessível (por meio de uma conexão http, por exemplo).
vikingsteve

Respostas:

157

Para o benefício da comunidade, já que este tópico é o principal no Google ao pesquisar por
" validador de url java "


A captura de exceções é cara e deve ser evitada sempre que possível. Se você deseja apenas verificar se sua String é uma URL válida, você pode usar a classe UrlValidator do projeto Apache Commons Validator .

Por exemplo:

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");
}
Yonatan
fonte
37
Essa classe URLValidator está marcada como obsoleta. O URLValidator recomendado está no pacote de rotinas: commons.apache.org/validator/apidocs/org/apache/commons/…
Spektr
6
@Spektr Consertei o link. Obrigado.
Yonatan,
18
Não consigo ver como isso é API padrão
b1nary.atr0phy
2
UrlValidator tem seu próprio conjunto de problemas conhecidos. Existe uma biblioteca alternativa que está sendo mantida de forma mais ativa?
Alex Averbuch
9
@AlexAverbuch: você pode descrever quais são os problemas com UrlValidator? Não é muito útil apenas dizer que eles existem, mas não dizer o que são.
cdmckay
33

Você precisa criar um URLobjeto e um URLConnectionobjeto. O código a seguir testará o formato do URL e se uma conexão pode ser estabelecida:

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
}
Olly
fonte
Observe que há várias maneiras de verificar urls / problemas malformados. Por exemplo, se você for usar seu url para um new HttpGet(url), poderá pegar os IllegalArgumentException HttpGet(...)lançamentos se houver um url malformado. E HttpResponsetambém atira em você se houver algum problema para obter os dados.
Peter Ajtai de
2
A conexão valida apenas a disponibilidade do host. Não tem nada a ver com a validade do URL.
Andrey Rodionov
2
MalformedURLException não é uma estratégia segura para testar a forma válida de um URL. Essa resposta é enganosa.
Martin
1
@Martin: você pode explicar por que não é seguro?
Jeroen Vannevel
28
Isso é muito, muito caro. openConnection / connect realmente tentará se conectar ao recurso http. Essa deve ser uma das maneiras mais caras que já vi para verificar um URL.
Glenn Bech
33

A java.net.URLclasse, na verdade, não é uma boa maneira de validar URLs. nãoMalformedURLException é lançado em todos os URLs malformados durante a construção. Pegando em não valida URL seja, apenas dizer-tempo ou não a conexão pode ser estabelecida.IOExceptionjava.net.URL#openConnection().connect()

Considere esta parte do código:

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

..que não lança nenhuma exceção.

Eu recomendo usar alguma API de validação implementada usando uma gramática livre de contexto, ou em uma validação muito simplificada apenas use expressões regulares. No entanto, preciso que alguém sugira uma API superior ou padrão para isso, só recentemente comecei a pesquisar por mim mesmo.

Nota Foi sugerido que, URL#toURI()em combinação com o tratamento da exceção, java.net. URISyntaxExceptionpode facilitar a validação de URLs. No entanto, esse método captura apenas um dos casos muito simples acima.

A conclusão é que não existe um analisador de URL java padrão para validar URLs.

Martin
fonte
Você encontrou uma solução para este problema ??
kidd0
@ bi0s.kidd0 Existem várias bibliotecas que podem ser usadas, mas decidimos lançar a nossa própria. Não está completo, mas pode analisar o que nos interessa, incluindo URLs contendo domínios ou IPs (v4 e v6). github.com/jajja/arachne
Martin
15

Usando apenas a API padrão, passe a string para um URLobjeto e converta-a em um URIobjeto. Isso determinará com precisão a validade do URL de acordo com o padrão RFC2396.

Exemplo:

public boolean isValidURL(String url) {

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

    return true;
}
b1nary.atr0phy
fonte
5
Observe que esse string-> url-> esquema de validação de uri informa que esses casos de teste são válidos: "http: //.com" " com ." "ftp: // :::: @ example.com" "http: /test.com" "http: test.com" "http: /:" Portanto, embora esta seja uma API padrão, as regras de validação que se aplicam podem não ser o que se espera.
DaveK
10

Use android.webkit.URLUtilno Android:

URLUtil.isValidUrl(URL_STRING);

Nota: É apenas verificar o esquema inicial da URL, não se a URL inteira é válida.

penduDev
fonte
2
Apenas se você estiver trabalhando em um aplicativo Android, é claro.
miva2
8

Existe uma maneira de realizar a validação de URL em estrita conformidade com os padrões em Java, sem recorrer a bibliotecas de terceiros:

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

O construtor de URIverifica se urlé um URI válido e a chamada para parseServerAuthoritygarante que é um URL (absoluto ou relativo) e não um URN.

dened
fonte
A exceção é lançada "Se o componente de autoridade deste URI for definido, mas não puder ser analisado como uma autoridade baseada no servidor de acordo com RFC 2396". Embora seja muito melhor do que a maioria das outras propostas, não pode validar um URL.
Martin de
@Martin, você se esqueceu da validação no construtor. Como escrevi, a combinação da URIchamada do construtor e a parseServerAuthoritychamada valida a URL, não parseServerAuthoritysozinha.
denunciado em
1
Você pode encontrar exemplos nesta página que foram validados incorretamente por sua sugestão. Consulte a documentação e, se não for projetada para o uso pretendido, não promova para explorá-la.
Martin
@Martin, você pode ser mais específico? Quais exemplos em sua opinião são validados incorretamente por este método?
denunciado
1
@Asu sim. O segundo ://vem depois do host, :introduz o número da porta, que pode estar vazia de acordo com a sintaxe. //é uma parte do caminho com um segmento vazio, que também é válido. Se você inserir este endereço em seu navegador, ele tentará abri-lo (mas provavelmente não encontrará o servidor chamado https;)).
denunciado em
2

É importante apontar que o objeto URL trata tanto da validação quanto da conexão. Então, apenas os protocolos para os quais um manipulador foi fornecido em sun.net.www.protocol são autorizados ( arquivo , ftp , gopher , http , https , jar , mailto , netdoc ) são válidos. Por exemplo, tente fazer um novo URL com o protocolo ldap :

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

Você receberá um java.net.MalformedURLException: unknown protocol: ldap.

Você precisa implementar seu próprio manipulador e registrá-lo por meio URL.setURLStreamHandlerFactory(). Um exagero se você deseja apenas validar a sintaxe da URL, uma expressão regular parece ser uma solução mais simples.

Doc Davluz
fonte
1

Tem certeza de que está usando o proxy correto como propriedades do sistema?

Além disso, se você estiver usando 1.5 ou 1.6, poderá passar uma instância java.net.Proxy para o método openConnection (). Isso é mais elegante 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);
NickDK
fonte
Por que isso seria elegante ou até correto? Ele usa recursos caros quando funciona e não funciona porque um URL correto não está disponível para conexão quando testado.
Martin
0

Acho que a melhor resposta é do usuário @ b1nary.atr0phy. De alguma forma, eu recomendo combinar o método da resposta b1nay.atr0phy com uma regex para cobrir todos os casos possíveis.

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;  

    }
Genaut
fonte
1
Existem alguns problemas com este regex: 1. URLs sem o prefixo são inválidos, (por exemplo, "stackoverflow.com"), isso também inclui URLs com dois sufixos se estiverem faltando o prefixo (por exemplo, "amazon.co.uk "). 2. Os IPs são sempre inválidos (por exemplo, " 127.0.0.1" ), independentemente de usarem o prefixo ou não. Eu sugiro usar "((http|https|ftp)://)?((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+"( fonte ). A única desvantagem dessa regex é que, por exemplo, "127.0..0.1" e "127.0" são válidos.
Neph
-2

Obrigado. Abrir a conexão de URL passando o Proxy conforme sugerido por NickDK funciona bem.

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

No entanto, as propriedades do sistema não funcionam como mencionei anteriormente.

Obrigado novamente.

Atenciosamente, Keya

Keya
fonte