【问题标题】:HTTP URL Address Encoding in JavaJava中的HTTP URL地址编码
【发布时间】:2010-10-17 23:10:27
【问题描述】:

我的 Java 独立应用程序从用户那里获得了一个 URL(它指向一个文件),我需要点击它并下载它。我面临的问题是我无法正确编码 HTTP URL 地址...

例子:

URL:  http://search.barnesandnoble.com/booksearch/first book.pdf

java.net.URLEncoder.encode(url.toString(), "ISO-8859-1");

返回我:

http%3A%2F%2Fsearch.barnesandnoble.com%2Fbooksearch%2Ffirst+book.pdf

但是,我想要的是

http://search.barnesandnoble.com/booksearch/first%20book.pdf

(空格替换为 %20)

我猜URLEncoder 不是为编码 HTTP URL 而设计的……JavaDoc 说“用于 HTML 表单编码的实用程序类”……还有其他方法吗?

【问题讨论】:

  • 吹毛求疵:根据定义包含空格字符的字符串不是 URI。所以你要找的是实现Section 2.1 of RFC 3986中定义的URI转义的代码。
  • 行为完全正确。 URL 编码是将某些内容转换为可以作为 URL 参数安全传递的字符串,并且根本不会被解释为 URL。而您希望它只转换 URL 的一小部分。

标签: java http urlencode


【解决方案1】:

是的,URL 编码将对该字符串进行编码,以便将其在 url 中正确传递到最终目的地。例如,您不能拥有http://stackoverflow.com?url=http://yyy.com。 UrlEncoding 参数将修复该参数值。

所以我有两个选择:

  1. 您是否有权访问与域分开的路径?如果是这样,您可以简单地对路径进行 UrlEncode。但是,如果不是这种情况,那么选项 2 可能适合您。

  2. 获取 commons-httpclient-3.1。这有一个类 URIUtil:

    System.out.println(URIUtil.encodePath("http://example.com/xy", "ISO-8859-1"));

这将准确地输出您要查找的内容,因为它只会对 URI 的路径部分进行编码。

仅供参考,您需要 commons-codec 和 commons-logging 才能使此方法在运行时工作。

【讨论】:

  • 旁注 apache commons 显然停止在 4.x 分支中维护 URIUtil,建议您改用 JDK 的 URI 类。只是意味着你必须自己分解字符串。
  • 2) 正是这里也建议stackoverflow.com/questions/5330104/…我也用URIUtil解决方案
【解决方案2】:

URLEncoding 可以很好地编码 HTTP URL,正如您不幸发现的那样。您传入的字符串“http://search.barnesandnoble.com/booksearch/firstbook.pdf”已正确且完整地编码为 URL 编码形式。您可以将返回的整个 gobbledigook 长字符串作为 URL 中的参数传递,并且可以将其解码回您传入的字符串。

听起来您想做一些不同于将整个 URL 作为参数传递的事情。据我所知,您正在尝试创建一个看起来像“http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn”的搜索 URL。您唯一需要编码的是“whateverTheUserPassesIn”位,所以也许您需要做的就是这样:

String url = "http://search.barnesandnoble.com/booksearch/" + 
       URLEncoder.encode(userInput,"UTF-8");

这应该会产生对你更有效的东西。

【讨论】:

  • 这会将 userInput 中的空格替换为“+”。发帖人需要将它们替换为“%20”。
  • @vocaro:这是一个很好的观点。 URLEncoder 转义就像参数是查询参数一样,而不是 URL 的其余部分。
【解决方案3】:

java.net.URI 类可以提供帮助;在您找到的 URL 文档中

请注意,URI 类在某些情况下会执行其组件字段的转义。管理 URL 编码和解码的推荐方法是使用 URI

使用具有多个参数的构造函数之一,例如:

URI uri = new URI(
    "http", 
    "search.barnesandnoble.com", 
    "/booksearch/first book.pdf",
    null);
URL url = uri.toURL();
//or String request = uri.toString();

(URI的单参数构造函数不会转义非法字符)


只有非法字符会被上面的代码转义——它不会转义非 ASCII 字符(参见 fatih 的评论)。
toASCIIString 方法可用于获取仅包含 US-ASCII 字符的字符串:

URI uri = new URI(
    "http", 
    "search.barnesandnoble.com", 
    "/booksearch/é",
    null);
String request = uri.toASCIIString();

对于带有 http://www.google.com/ig/api?weather=São Paulo 之类查询的 URL,请使用 5 参数版本的构造函数:

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();

【讨论】:

  • 请注意,这里提到的 URI 类来自 "org.apache.commons.httpclient.URI" 而不是 "java.net" , "java.net" 不是 URI 不接受非法字符,除非您将使用从其组件构建 URL 的构造函数,例如下面的 Matt 评论中提到的方式
  • @Mohamed:我提到并用于测试的类实际上是 java.net.URI:它运行良好(Java 1.6)。如果它不是标准的 Java 类名,我会提到完全限定的类名,并且链接指向 java.net.URI 的文档。而且,根据 Sudhakar 的评论,它在不包含任何“公共库”的情况下解决了问题!
  • URI uri = new URI("http", "search.barnesandnoble.com", "/booksearch/é",null);不使用此示例进行正确转义?这应该用 % escapes 转义
  • @fatih - 没错,谢谢!通常这不应该是一个问题,但有一个简单的解决方案 - 几乎和我之前写的一样。请参阅第二次编辑。
  • @Carlos 谢谢编辑。现在它确实转义但不正确转义。它应该为路径参数的 char 的 HEX 值添加一个 %,这意味着应该将 é char 转换为 %e9
【解决方案4】:

请注意,上面的大多数答案都是不正确的。

URLEncoder 类,尽管是名称,但不是这里需要的。不幸的是,Sun 如此讨厌地命名这个类。 URLEncoder 用于将数据作为参数传递,而不是用于对 URL 本身进行编码。

换句话说,"http://search.barnesandnoble.com/booksearch/first book.pdf" 是 URL。例如,参数将是"http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that"。这些参数是您将使用 URLEncoder 的参数。

以下两个示例突出了两者之间的差异。

根据 HTTP 标准,以下会产生错误的参数。请注意与号 (&) 和加号 (+) 的编码不正确。

uri = new URI("http", null, "www.google.com", 80, 
"/help/me/book name+me/", "MY CRZY QUERY! +&+ :)", null);

// URI: http://www.google.com:80/help/me/book%20name+me/?MY%20CRZY%20QUERY!%20+&+%20:)

以下将生成正确的参数,并正确编码查询。注意空格、& 和加号。

uri = new URI("http", null, "www.google.com", 80, "/help/me/book name+me/", URLEncoder.encode("MY CRZY QUERY! +&+ :)", "UTF-8"), null);

// URI: http://www.google.com:80/help/me/book%20name+me/?MY+CRZY+QUERY%2521+%252B%2526%252B+%253A%2529

【讨论】:

  • 没错,URI构造函数已经对查询字符串进行了编码,根据文档docs.oracle.com/javase/1.4.2/docs/api/java/net/…, java.lang.String, java.lang.String, int, java.lang.String, java.lang .String, java.lang.String)
  • @Draemon 答案是正确的,但是以一种不常见的方式使用了查询字符串;一个更正常的例子可能是query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value)。文档只是说“引用了任何不是合法 URI 字符的字符”。
  • 我同意马特的观点。如果您在浏览器中键入此 URL:“google.com/help/me/bookname+me/?MY CRZY QUERY!+&+ :)”,它会自动对空格进行编码,但“&”用作查询值分隔符,“+”是迷路了。
  • 不幸的是,这个答案也是错误的,因为它对事物进行了双重编码。使用多参数 URI 构造函数时,如果路径中有斜杠,或者查询参数或值中有“&”或“=”,则要么无法对它们进行编码,要么对它们进行双重编码。
【解决方案5】:

如果您的 URL 中有编码的“/” (%2F),仍然存在问题。

RFC 3986 - 第 2.2 节说:“如果 URI 组件的数据与保留字符作为分隔符的用途发生冲突,则冲突数据必须在 URI 形成之前进行百分比编码。” (RFC 3986 - 第 2.2 节)

但是Tomcat有一个问题:

http://tomcat.apache.org/security-6.html - 在 Apache Tomcat 6.0.10 中已修复

重要:目录遍历 CVE-2007-0450

Tomcat 允许“\”、“%2F”和“%5C” [...]。

以下 Java 系统属性 已添加到 Tomcat 以提供 对处理的附加控制 URL 中的路径分隔符(两个选项 默认为假):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: 真|假
  • org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: 真|假

由于无法保证 所有 URL 都由 Tomcat 处理为 他们在代理服务器,Tomcat 应该始终确保没有 限制上下文访问的代理是 用过。

影响:6.0.0-6.0.9

所以如果你有一个带有 %2F 字符的 URL,Tomcat 会返回:“400 Invalid URI: noSlash”

您可以在 Tomcat 启动脚本中切换 bugfix:

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 

【讨论】:

    【解决方案6】:

    我开发的解决方案比其他任何解决方案都稳定:

    public class URLParamEncoder {
    
        public static String encode(String input) {
            StringBuilder resultStr = new StringBuilder();
            for (char ch : input.toCharArray()) {
                if (isUnsafe(ch)) {
                    resultStr.append('%');
                    resultStr.append(toHex(ch / 16));
                    resultStr.append(toHex(ch % 16));
                } else {
                    resultStr.append(ch);
                }
            }
            return resultStr.toString();
        }
    
        private static char toHex(int ch) {
            return (char) (ch < 10 ? '0' + ch : 'A' + ch - 10);
        }
    
        private static boolean isUnsafe(char ch) {
            if (ch > 128 || ch < 0)
                return true;
            return " %$&+,/:;=?@<>#%".indexOf(ch) >= 0;
        }
    
    }
    

    【讨论】:

    • 这也需要您将 url 分成几部分。计算机无法知道要对 url 的哪一部分进行编码。看我上面的编辑
    • @fmucar 感谢这段代码!应该注意的是,这不是 UTF-8。要获得 UTF-8,只需使用 String utf8Input = new String(Charset.forName("UTF-8").encode(input).array()); 预处理输入(取自 here
    • 这个解决方案实际上还将“http://”部分编码为“http%3A%2F%2F”,这是最初的问题试图避免的。
    • 您只传递您需要编码的内容,而不是整个 URL。没有办法传递一个完整的 URL 字符串并期望正确的编码。在所有情况下,您都需要将 url 分解为其逻辑部分。
    • 我对这个答案有疑问,因为它不会将不安全的字符编码为 UTF-8。但可能取决于对等应用程序。
    【解决方案7】:

    我创建了一个新项目来帮助构建 HTTP URL。该库将自动对路径段和查询参数进行 URL 编码。

    您可以在https://github.com/Widen/urlbuilder查看源代码并下载二进制文件

    本题的示例网址:

    new UrlBuilder("search.barnesandnoble.com", "booksearch/first book.pdf").toString()
    

    生产

    http://search.barnesandnoble.com/booksearch/first%20book.pdf

    【讨论】:

      【解决方案8】:

      我同意马特的观点。确实,我从来没有在教程中看到过很好的解释,但是一个问题是如何对 URL 路径进行编码,而一个非常不同的问题是如何对附加到 URL 的参数进行编码(查询部分,在 "? “ 象征)。它们使用相似的编码,但不一样。

      专门用于空白字符的编码。 URL 路径需要将其编码为 %20,而查询部分允许使用 %20 以及“+”号。最好的办法是我们自己使用 Web 浏览器针对我们的 Web 服务器对其进行测试。

      对于这两种情况,我总是会编码 COMPONENT BY COMPONENT,而不是整个字符串。确实 URLEncoder 允许查询部分。对于路径部分,您可以使用类 URI,尽管在这种情况下它要求提供整个字符串,而不是单个组件。

      无论如何,我认为避免这些问题的最佳方法是使用个人非冲突设计。如何?例如,我绝不会使用 a-Z、A-Z、0-9 和 _ 以外的其他字符来命名目录或参数。这样,唯一需要对每个参数的值进行编码,因为它可能来自用户输入并且使用的字符是未知的。

      【讨论】:

      • 使用问题中的 URL 的示例代码将是一个很好的答案
      【解决方案9】:

      不幸的是,org.apache.commons.httpclient.util.URIUtil 已被弃用,replacement org.apache.commons.codec.net.URLCodec 的编码适用于表单帖子,而不是实际的 URL。所以我不得不编写自己的函数,它只做一个组件(不适合包含 ? 和 & 的整个查询字符串)

      public static String encodeURLComponent(final String s)
      {
        if (s == null)
        {
          return "";
        }
      
        final StringBuilder sb = new StringBuilder();
      
        try
        {
          for (int i = 0; i < s.length(); i++)
          {
            final char c = s.charAt(i);
      
            if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
                ((c >= '0') && (c <= '9')) ||
                (c == '-') ||  (c == '.')  || (c == '_') || (c == '~'))
            {
              sb.append(c);
            }
            else
            {
              final byte[] bytes = ("" + c).getBytes("UTF-8");
      
              for (byte b : bytes)
              {
                sb.append('%');
      
                int upper = (((int) b) >> 4) & 0xf;
                sb.append(Integer.toHexString(upper).toUpperCase(Locale.US));
      
                int lower = ((int) b) & 0xf;
                sb.append(Integer.toHexString(lower).toUpperCase(Locale.US));
              }
            }
          }
      
          return sb.toString();
        }
        catch (UnsupportedEncodingException uee)
        {
          throw new RuntimeException("UTF-8 unsupported!?", uee);
        }
      }
      

      【讨论】:

      • 来吧,必须有一个库可以做到这一点。
      【解决方案10】:

      除了 Carlos Heuberger 的回复: 如果需要与默认值 (80) 不同的值,则应使用 7 参数构造函数:

      URI uri = new URI(
              "http",
              null, // this is for userInfo
              "www.google.com",
              8080, // port number as int
              "/ig/api",
              "weather=São Paulo",
              null);
      String request = uri.toASCIIString();
      

      【讨论】:

        【解决方案11】:

        我将在这里添加一个针对 Android 用户的建议。您可以这样做,以避免必须获取任何外部库。此外,上述某些答案中建议的所有搜索/替换字符解决方案都是危险的,应该避免。

        试试这个:

        String urlStr = "http://abc.dev.domain.com/0007AC/ads/800x480 15sec h.264.mp4";
        URL url = new URL(urlStr);
        URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
        url = uri.toURL();
        

        您可以看到,在这个特定的 URL 中,我需要对这些空格进行编码,以便可以将其用于请求。

        这利用了 Android 类中提供给您的几个功能。首先,URL 类可以将 url 分解为其适当的组件,因此您无需进行任何字符串搜索/替换工作。其次,当您通过组件而不是从单个字符串构造 URI 时,这种方法利用了 URI 类特性正确转义组件。

        这种方法的美妙之处在于,您可以获取任何有效的 url 字符串并使其工作,而无需自己掌握任何特殊知识。

        【讨论】:

        • 不错的方法,但我想指出,此代码不会阻止 双重编码,例如%20 被编码为 %2520。 Scott's answer 不会受此影响。
        • 或者如果你只想做路径引用:new URI(null, null, "/path with spaces", null, null).toString()
        • @Stallman 如果你的文件名中包含#,那么 URL 类会将其放入“ref”(相当于 URI 类中的“fragment”)。您可以检测 URL.getRef() 是否返回可能被视为路径一部分的内容,并将 URL.getPath() + "#" + URL.getRef() 作为“路径”参数传递,并将 null 作为“片段”传递" URI 类 7 参数构造函数的参数。默认情况下,# 之后的字符串被视为引用(或锚点)。
        • 很好的答案,我有简单的网址,它对我有用。虽然我不认为它非常特定于android。我使用了java.net.URIjava.net.URL,这个答案运行良好。我什至可以对此进行单元测试。
        【解决方案12】:

        如果你有一个 URL,你可以将 url.toString() 传递给这个方法。首先解码,避免双重编码(例如,编码空格会导致 %20,编码百分号会导致 %25,所以双重编码会将空格变成 %2520)。然后,使用上面解释的 URI,添加 URL 的所有部分(这样您就不会删除查询参数)。

        public URL convertToURLEscapingIllegalCharacters(String string){
            try {
                String decodedURL = URLDecoder.decode(string, "UTF-8");
                URL url = new URL(decodedURL);
                URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); 
                return uri.toURL(); 
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            }
        }
        

        【讨论】:

        • 当您将字符串作为“google.co.in/search?q=123%!123”传递时,URLDecoder.decode(string, "UTF-8") 失败并出现 IllegalArgumentException。这是一个有效的 URL。我猜这个 API 在 % 用作数据而不是编码字符时不起作用。
        【解决方案13】:

        怎么样:

        public String UrlEncode(String in_) {

        String retVal = "";
        
        try {
            retVal = URLEncoder.encode(in_, "UTF8");
        } catch (UnsupportedEncodingException ex) {
            Log.get().exception(Log.Level.Error, "urlEncode ", ex);
        }
        
        return retVal;
        

        }

        【讨论】:

        • URLEncoder 不能用于转义有效的 URL 字符。仅用于编码表单。
        【解决方案14】:

        也许可以在 org.springframework.web.util 中尝试UriUtils

        UriUtils.encodeUri(input, "UTF-8")
        

        【讨论】:

          【解决方案15】:

          我开发了一个用于此目的的库:galimatias。它解析 URL 的方式与 Web 浏览器相同。也就是说,如果一个 URL 在浏览器中工作,它会被 galimatias 正确解析。

          在这种情况下:

          // Parse
          io.mola.galimatias.URL.parse(
              "http://search.barnesandnoble.com/booksearch/first book.pdf"
          ).toString()
          

          会给你:http://search.barnesandnoble.com/booksearch/first%20book.pdf。当然这是最简单的情况,但它适用于任何东西,远远超出java.net.URI

          您可以查看:https://github.com/smola/galimatias

          【讨论】:

          • 我不知道为什么这个答案被否决了这么多。这个库虽然占用空间有点大,但正是我需要的。
          【解决方案16】:

          您可以使用这样的功能。根据您的需要完成并修改它:

          /**
               * Encode URL (except :, /, ?, &, =, ... characters)
               * @param url to encode
               * @param encodingCharset url encoding charset
               * @return encoded URL
               * @throws UnsupportedEncodingException
               */
              public static String encodeUrl (String url, String encodingCharset) throws UnsupportedEncodingException{
                      return new URLCodec().encode(url, encodingCharset).replace("%3A", ":").replace("%2F", "/").replace("%3F", "?").replace("%3D", "=").replace("%26", "&");
              }
          

          使用示例:

          String urlToEncode = ""http://www.growup.com/folder/intérieur-à_vendre?o=4";
          Utils.encodeUrl (urlToEncode , "UTF-8")
          

          结果是:http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4

          【讨论】:

          • 如果没有 URLCodec,这个答案是不完整的。
          • 支持 .replace() 链接,它并不理想,但对于基本的临时用例来说已经足够了
          【解决方案17】:

          我阅读了以前的答案来编写自己的方法,因为使用以前答案的解决方案无法正常工作,这对我来说看起来不错,但是如果您能找到不适用于此的 URL,请告诉我知道。

          public static URL convertToURLEscapingIllegalCharacters(String toEscape) throws MalformedURLException, URISyntaxException {
                      URL url = new URL(toEscape);
                      URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
                      //if a % is included in the toEscape string, it will be re-encoded to %25 and we don't want re-encoding, just encoding
                      return new URL(uri.toString().replace("%25", "%"));
          }
          

          【讨论】:

          【解决方案18】:

          您还可以使用GUAVA 和路径转义器: UrlEscapers.urlFragmentEscaper().escape(relativePath)

          【讨论】:

            【解决方案19】:

            我遇到了同样的问题。通过取消解决这个问题:

            android.net.Uri.encode(urlString, ":/");
            

            它对字符串进行编码但跳过“:”和“/”。

            【讨论】:

              【解决方案20】:

              如果有人不想在他们的项目中添加依赖项,这些函数可能会有所帮助。

              我们将 URL 的“路径”部分传递到此处。您可能不想将完整的 URL 作为参数传递(查询字符串需要不同的转义等)。

              /**
               * Percent-encodes a string so it's suitable for use in a URL Path (not a query string / form encode, which uses + for spaces, etc)
               */
              public static String percentEncode(String encodeMe) {
                  if (encodeMe == null) {
                      return "";
                  }
                  String encoded = encodeMe.replace("%", "%25");
                  encoded = encoded.replace(" ", "%20");
                  encoded = encoded.replace("!", "%21");
                  encoded = encoded.replace("#", "%23");
                  encoded = encoded.replace("$", "%24");
                  encoded = encoded.replace("&", "%26");
                  encoded = encoded.replace("'", "%27");
                  encoded = encoded.replace("(", "%28");
                  encoded = encoded.replace(")", "%29");
                  encoded = encoded.replace("*", "%2A");
                  encoded = encoded.replace("+", "%2B");
                  encoded = encoded.replace(",", "%2C");
                  encoded = encoded.replace("/", "%2F");
                  encoded = encoded.replace(":", "%3A");
                  encoded = encoded.replace(";", "%3B");
                  encoded = encoded.replace("=", "%3D");
                  encoded = encoded.replace("?", "%3F");
                  encoded = encoded.replace("@", "%40");
                  encoded = encoded.replace("[", "%5B");
                  encoded = encoded.replace("]", "%5D");
                  return encoded;
              }
              
              /**
               * Percent-decodes a string, such as used in a URL Path (not a query string / form encode, which uses + for spaces, etc)
               */
              public static String percentDecode(String encodeMe) {
                  if (encodeMe == null) {
                      return "";
                  }
                  String decoded = encodeMe.replace("%21", "!");
                  decoded = decoded.replace("%20", " ");
                  decoded = decoded.replace("%23", "#");
                  decoded = decoded.replace("%24", "$");
                  decoded = decoded.replace("%26", "&");
                  decoded = decoded.replace("%27", "'");
                  decoded = decoded.replace("%28", "(");
                  decoded = decoded.replace("%29", ")");
                  decoded = decoded.replace("%2A", "*");
                  decoded = decoded.replace("%2B", "+");
                  decoded = decoded.replace("%2C", ",");
                  decoded = decoded.replace("%2F", "/");
                  decoded = decoded.replace("%3A", ":");
                  decoded = decoded.replace("%3B", ";");
                  decoded = decoded.replace("%3D", "=");
                  decoded = decoded.replace("%3F", "?");
                  decoded = decoded.replace("%40", "@");
                  decoded = decoded.replace("%5B", "[");
                  decoded = decoded.replace("%5D", "]");
                  decoded = decoded.replace("%25", "%");
                  return decoded;
              }
              

              和测试:

              @Test
              public void testPercentEncode_Decode() {
                  assertEquals("", percentDecode(percentEncode(null)));
                  assertEquals("", percentDecode(percentEncode("")));
              
                  assertEquals("!", percentDecode(percentEncode("!")));
                  assertEquals("#", percentDecode(percentEncode("#")));
                  assertEquals("$", percentDecode(percentEncode("$")));
                  assertEquals("@", percentDecode(percentEncode("@")));
                  assertEquals("&", percentDecode(percentEncode("&")));
                  assertEquals("'", percentDecode(percentEncode("'")));
                  assertEquals("(", percentDecode(percentEncode("(")));
                  assertEquals(")", percentDecode(percentEncode(")")));
                  assertEquals("*", percentDecode(percentEncode("*")));
                  assertEquals("+", percentDecode(percentEncode("+")));
                  assertEquals(",", percentDecode(percentEncode(",")));
                  assertEquals("/", percentDecode(percentEncode("/")));
                  assertEquals(":", percentDecode(percentEncode(":")));
                  assertEquals(";", percentDecode(percentEncode(";")));
              
                  assertEquals("=", percentDecode(percentEncode("=")));
                  assertEquals("?", percentDecode(percentEncode("?")));
                  assertEquals("@", percentDecode(percentEncode("@")));
                  assertEquals("[", percentDecode(percentEncode("[")));
                  assertEquals("]", percentDecode(percentEncode("]")));
                  assertEquals(" ", percentDecode(percentEncode(" ")));
              
                  // Get a little complex
                  assertEquals("[]]", percentDecode(percentEncode("[]]")));
                  assertEquals("a=d%*", percentDecode(percentEncode("a=d%*")));
                  assertEquals(")  (", percentDecode(percentEncode(")  (")));
                  assertEquals("%21%20%2A%20%27%20%28%20%25%20%29%20%3B%20%3A%20%40%20%26%20%3D%20%2B%20%24%20%2C%20%2F%20%3F%20%23%20%5B%20%5D%20%25",
                                  percentEncode("! * ' ( % ) ; : @ & = + $ , / ? # [ ] %"));
                  assertEquals("! * ' ( % ) ; : @ & = + $ , / ? # [ ] %", percentDecode(
                                  "%21%20%2A%20%27%20%28%20%25%20%29%20%3B%20%3A%20%40%20%26%20%3D%20%2B%20%24%20%2C%20%2F%20%3F%20%23%20%5B%20%5D%20%25"));
              
                  assertEquals("%23456", percentDecode(percentEncode("%23456")));
              
              }
              

              【讨论】:

              • 谢谢你,但是我需要做什么来编码一个空格 -> 按照你的例子使用 %20 代替?
              • 已更新以将空格记为 %20
              【解决方案21】:

              使用以下标准 Java 解决方案(通过 Web Plattform Tests 提供的大约 100 个测试用例):

              0. Test if URL is already encoded.

              1. 将 URL 拆分为结构部分。使用java.net.URL

              2.正确编码每个结构部分!

              3. 使用IDN.toASCII(putDomainNameHere)Punycode 编码主机名!

              4. 使用java.net.URI.toASCIIString() 进行百分比编码,NFC 编码的 unicode -(最好是 NFKC!)。

              在此处了解更多信息:https://stackoverflow.com/a/49796882/1485527

              【讨论】:

                【解决方案22】:

                我把上面的内容做了一些改动。我首先喜欢积极的逻辑,我认为 HashSet 可能比其他一些选项提供更好的性能,比如搜索字符串。虽然,我不确定自动装箱的代价是否值得,但如果编译器针对 ASCII 字符进行优化,那么装箱的成本会很低。

                /***
                 * Replaces any character not specifically unreserved to an equivalent 
                 * percent sequence.
                 * @param s
                 * @return
                 */
                public static String encodeURIcomponent(String s)
                {
                    StringBuilder o = new StringBuilder();
                    for (char ch : s.toCharArray()) {
                        if (isSafe(ch)) {
                            o.append(ch);
                        }
                        else {
                            o.append('%');
                            o.append(toHex(ch / 16));
                            o.append(toHex(ch % 16));
                        }
                    }
                    return o.toString();
                }
                
                private static char toHex(int ch)
                {
                    return (char)(ch < 10 ? '0' + ch : 'A' + ch - 10);
                }
                
                // https://tools.ietf.org/html/rfc3986#section-2.3
                public static final HashSet<Character> UnreservedChars = new HashSet<Character>(Arrays.asList(
                        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
                        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
                        '0','1','2','3','4','5','6','7','8','9',
                        '-','_','.','~'));
                public static boolean isSafe(char ch)
                {
                    return UnreservedChars.contains(ch);
                }
                

                【讨论】:

                  【解决方案23】:

                  我用这个

                  org.apache.commons.text.StringEscapeUtils.escapeHtml4("my text % & < >");
                  

                  添加这个依赖

                   <dependency>
                          <groupId>org.apache.commons</groupId>
                          <artifactId>commons-text</artifactId>
                          <version>1.8</version>
                      </dependency>
                  

                  【讨论】:

                  • 这会转义 HTML 标签,但不会转义 URL
                  【解决方案24】:

                  如果你用的是spring,可以试试 org.springframework.web.util.UriUtils#encodePath

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-07-19
                    • 2012-11-05
                    • 1970-01-01
                    • 2010-09-24
                    相关资源
                    最近更新 更多