How to compare two URLs in java?

前端 未结 4 1225
逝去的感伤
逝去的感伤 2020-12-03 21:56

Here\'s a simple problem - given two urls, is there some built-in method, or an Apache library that decides whether they are (logically) equal?

For example, these tw

相关标签:
4条回答
  • 2020-12-03 22:10

    While URI.equals() (as well as the problematic URL.equals()) does not return true for these specific examples, I think it's the only case where equivalence can be assumed (because there is no empty path in the HTTP protocol).

    The URIs http://stackoverflow.com/foo and http://stackoverflow.com/foo/ can not be assumed to be equivalent.

    Maybe you can use URI.equals() wrapped in a utility method that handles this specific case explicitly.

    0 讨论(0)
  • 2020-12-03 22:14

    The following may work for you - it validates that 2 urls are equal, allows the parameters to be supplied in different orders, and allows a variety of options to be configured, that being:

    • Is the host case sensitive
    • Is the path case sensitive
    • Are query string parameters case sensitive
    • Are query string values case sensitive
    • Is the scheme case sensitive

    You can test it like so:

    class Main {
    
      public static void main(String[] args) 
      {
          UrlComparer urlComparer = new UrlComparer();
    
          expectResult(false, "key a case different", urlComparer.urlsMatch("//test.com?A=a&B=b", "//test.com?a=a&b=b"));
          expectResult(false, "key a case different", urlComparer.urlsMatch("https://WWW.TEST.COM?A=1&b=2", "https://www.test.com?b=2&a=1"));
          expectResult(false, "key a value different", urlComparer.urlsMatch("/test?a=2&A=A", "/test?a=A&a=2"));
          expectResult(false, "key a value different", urlComparer.urlsMatch("https://WWW.TEST.COM?A=a&b=2", "https://www.test.com?b=2&A=1"));
          expectResult(false, "null", urlComparer.urlsMatch("/test", null));
          expectResult(false, "null", urlComparer.urlsMatch(null, "/test"));
          expectResult(false, "port different", urlComparer.urlsMatch("//test.com:22?A=a&B=b", "//test.com:443?A=a&B=b"));
          expectResult(false, "port different", urlComparer.urlsMatch("https://WWW.TEST.COM:8443", "https://www.test.com"));
          expectResult(false, "protocol different", urlComparer.urlsMatch("http://WWW.TEST.COM:2121", "https://www.test.com:2121"));
          expectResult(false, "protocol different", urlComparer.urlsMatch("http://WWW.TEST.COM?A=a&b=2", "https://www.test.com?b=2&A=a"));
          expectResult(true, "both null", urlComparer.urlsMatch(null, null));
          expectResult(true, "host and scheme different case", urlComparer.urlsMatch("HTTPS://WWW.TEST.COM", "https://www.test.com"));
          expectResult(true, "host different case", urlComparer.urlsMatch("https://WWW.TEST.COM:443", "https://www.test.com"));
          expectResult(true, "identical urls", urlComparer.urlsMatch("//test.com:443?A=a&B=b", "//test.com:443?A=a&B=b"));
          expectResult(true, "identical urls", urlComparer.urlsMatch("/test?a=A&a=2", "/test?a=A&a=2"));
          expectResult(true, "identical urls", urlComparer.urlsMatch("https://www.test.com", "https://www.test.com"));
          expectResult(true, "parameter order changed", urlComparer.urlsMatch("https://www.test.com?a=1&b=2&c=522%2fMe", "https://www.test.com?c=522%2fMe&b=2&a=1"));
          expectResult(true, "parmeter order changed", urlComparer.urlsMatch("https://WWW.TEST.COM?a=1&b=2", "https://www.test.com?b=2&a=1"));
        }
    
        public static void expectResult(boolean expectedResult, String msg, boolean result)
        {
          if (expectedResult != result)
            throw new RuntimeException(msg);
        }
    }
    

    UrlComparer.java

    import java.net.URI;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.TreeMap;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.utils.URLEncodedUtils;
    
    public class UrlComparer
    {
        private boolean hostIsCaseSensitive = false;
        private boolean pathIsCaseSensitive = true;
        private boolean queryStringKeysAreCaseSensitive = true;
        private boolean queryStringValuesAreCaseSensitive = false;
        private boolean schemeIsCaseSensitive = false;
    
        public boolean urlsMatch(String url1, String url2)
        {
            try
            {
                if (Objects.equals(url1, url2))
                    return true;
    
                URI uri1 = new URI(url1);
                URI uri2 = new URI(url2);
    
                // Compare Query String Parameters
                Map<String, String> mapParams1 = getQueryStringParams(uri1);
                Map<String, String> mapParams2 = getQueryStringParams(uri2);
                if (!mapsAreEqual(mapParams1, mapParams2, getQueryStringValuesAreCaseSensitive()))
                    return false;
    
                // Compare scheme (http or https)
                if (!stringsAreEqual(uri1.getScheme(), uri2.getScheme(), getSchemeIsCaseSensitive()))
                    return false;
    
                // Compare host
                if (!stringsAreEqual(uri1.getHost(), uri2.getHost(), getHostIsCaseSensitive()))
                    return false;
    
                // Compare path
                if (!stringsAreEqual(uri1.getPath(), uri2.getPath(), getPathIsCaseSensitive()))
                    return false;
    
                // Compare ports
                if (!portsAreEqual(uri1, uri2))
                    return false;
    
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }
    
        protected Map<String, String> getQueryStringParams(URI uri)
        {
            Map<String, String> result = getListAsMap(URLEncodedUtils.parse(uri, "UTF-8"), getQueryStringKeysAreCaseSensitive());
    
            return result;
        }
    
        protected boolean stringsAreEqual(String s1, String s2, boolean caseSensitive)
        {
            // Eliminate null cases
            if (s1 == null || s2 == null)
            {
                if (s1 == s2)
                    return true;
    
                return false;
            }
    
            if (caseSensitive)
            {
                return s1.equals(s2);
            }
    
            return s1.equalsIgnoreCase(s2);
        }
    
        protected boolean mapsAreEqual(Map<String, String> map1, Map<String, String> map2, boolean caseSensitiveValues)
        {
            for (Map.Entry<String, String> entry : map1.entrySet())
            {
                String key = entry.getKey();
                String map1value = entry.getValue();
                String map2value = map2.get(key);
    
                if (!stringsAreEqual(map1value, map2value, caseSensitiveValues))
                    return false;
            }
            for (Map.Entry<String, String> entry : map2.entrySet())
            {
                String key = entry.getKey();
                String map2value = entry.getValue();
                String map1value = map2.get(key);
    
                if (!stringsAreEqual(map1value, map2value, caseSensitiveValues))
                    return false;
            }
    
            return true;
        }
    
        protected boolean portsAreEqual(URI uri1, URI uri2)
        {
            int port1 = uri1.getPort();
            int port2 = uri2.getPort();
    
            if (port1 == port2)
                return true;
    
            if (port1 == -1)
            {
                String scheme1 = (uri1.getScheme() == null ? "http" : uri1.getScheme()).toLowerCase();
                port1 = scheme1.equals("http") ? 80 : 443;
            }
            if (port2 == -1)
            {
                String scheme2 = (uri2.getScheme() == null ? "http" : uri2.getScheme()).toLowerCase();
                port2 = scheme2.equals("http") ? 80 : 443;
            }
    
            boolean result = (port1 == port2);
    
            return result;
        }
    
        protected Map<String, String> getListAsMap(List<NameValuePair> list, boolean caseSensitiveKeys)
        {
            Map<String, String> result;
    
            if (caseSensitiveKeys)
            {
                result = new HashMap<String, String>();
            }
            else
            {
                result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
            }
    
            for (NameValuePair param : list)
            {
                if (caseSensitiveKeys)
                {
                    if (!result.containsKey(param.getName()))
                        result.put(param.getName(), param.getValue());
                }
                else
                {
                    result.put(param.getName(), param.getValue());
                }
            }
    
            return result;
        }
    
        public boolean getSchemeIsCaseSensitive()
        {
            return schemeIsCaseSensitive;
        }
    
        public void setSchemeIsCaseSensitive(boolean schemeIsCaseSensitive)
        {
            this.schemeIsCaseSensitive = schemeIsCaseSensitive;
        }
    
        public boolean getHostIsCaseSensitive()
        {
            return hostIsCaseSensitive;
        }
    
        public void setHostIsCaseSensitive(boolean hostIsCaseSensitive)
        {
            this.hostIsCaseSensitive = hostIsCaseSensitive;
        }
    
        public boolean getPathIsCaseSensitive()
        {
            return pathIsCaseSensitive;
        }
    
        public void setPathIsCaseSensitive(boolean pathIsCaseSensitive)
        {
            this.pathIsCaseSensitive = pathIsCaseSensitive;
        }
    
        public boolean getQueryStringKeysAreCaseSensitive()
        {
            return queryStringKeysAreCaseSensitive;
        }
    
        public void setQueryStringKeysAreCaseSensitive(boolean queryStringKeysAreCaseSensitive)
        {
            this.queryStringKeysAreCaseSensitive = queryStringKeysAreCaseSensitive;
        }
    
        public boolean getQueryStringValuesAreCaseSensitive()
        {
            return queryStringValuesAreCaseSensitive;
        }
    
        public void setQueryStringValuesAreCaseSensitive(boolean queryStringValuesAreCaseSensitive)
        {
            this.queryStringValuesAreCaseSensitive = queryStringValuesAreCaseSensitive;
        }
    
    }
    
    0 讨论(0)
  • 2020-12-03 22:17

    sameFile

    public boolean sameFile(URL other)Compares two URLs,

    excluding the fragment component. Returns true if this URL and the other argument are equal without taking the fragment component into consideration.

    Parameters: other - the URL to compare against. Returns: true if they reference the same remote object; false otherwise.

    also please go through this link

    http://download.oracle.com/javase/6/docs/api/java/net/URL.html#sameFile(java.net.URL)

    As iam unable to add comment , browser throwing Javascript error. so iam adding my comment here. regret for inconvience.

    //this what i suggeted

    >URL url1 = new URL("http://stackoverflow.com/foo");
    
    >URL url2 = new URL("http://stackoverflow.com/foo/");
    
    >System.out.println(url1.sameFile(url2));
    
    // this is suggested by Joachim Sauer 
    >URI uri = new URI("http://stackoverflow.com/foo/");
    >System.out.println(uri.equals("http://stackoverflow.com/foo"));
    
    // Both are giving same result
    

    so Joachim Sauer check once.

    0 讨论(0)
  • 2020-12-03 22:24

    URL::equals reference

    URL urlOne = new URL("http://stackoverflow.com");
    URL urlTwo = new URL("http://stackoverflow.com/");
    
    if( urlOne.equals(urlTwo) )
    {
        // ....
    }
    

    Note from docs -

    Two URL objects are equal if they have the same protocol, reference equivalent hosts, have the same port number on the host, and the same file and fragment of the file.

    Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null.

    Since hosts comparison requires name resolution, this operation is a blocking operation.

    Note: The defined behavior for equals is known to be inconsistent with virtual hosting in HTTP.

    So, instead you should prefer URI::equals reference as @Joachim suggested.

    0 讨论(0)
提交回复
热议问题