【问题标题】:HttpClient and non-ASCII URL characters (á,é,í,ó,ú)HttpClient 和非 ASCII URL 字符 (á,é,í,ó,ú)
【发布时间】:2011-08-25 17:38:07
【问题描述】:

'老读者,第一次发帖'在这里。

我正在为我管理的西班牙语 Wiki 创建bot。我想从头开始制作它,因为我制作它的目的之一是练习 Java。但是,在尝试使用 HttpClient 向包含非 ASCII 字符(例如 á、é、í、ó 或 ú)的 URI 发出 GET 请求时,我遇到了一些麻烦。

String url = "http://es.metroid.wikia.com/api.php?action=query&list=categorymembers&cmtitle=Categoría:Mejoras de las Botas"
method = new GetMethod(url);
client.executeMethod(method);

当我执行上述操作时,GetMethod 抱怨 URI:

Exception in thread "main" java.lang.IllegalArgumentException: Invalid uri 'http://es.pruebaloca.wikia.com/api.php?action=query&list=categorymembers&cmtitle=Categoría:Mejoras%20de%20las%20Botas&cmlimit=500&format=xml': Invalid query
    at org.apache.commons.httpclient.HttpMethodBase.<init>(HttpMethodBase.java:222)
    at org.apache.commons.httpclient.methods.GetMethod.<init>(GetMethod.java:89)
    at net.metroidover.categorybot.http.HttpRequest.request(HttpRequest.java:69)
    at net.metroidover.categorybot.http.HttpRequest.request(HttpRequest.java:120)
    at net.metroidover.categorybot.http.Action.getCategoryMembers(Action.java:38)
    at net.metroidover.categorybot.bot.BotComponent.<init>(BotComponent.java:58)
    at net.metroidover.categorybot.bot.BotComponent.main(BotComponent.java:80)

请注意,在堆栈跟踪中显示的 URI 中,空格被编码为 %20,而 ís 保持原样。完全相同的 URI 在浏览器上完美运行,但我无法绕过 GetMethod 接受它。

我也尝试过以下操作:

URI uri = new URI(url, false);
method = new GetMethod(uri.getEscapedURI());
client.executeMethod(method);

这样,URI 转义了 is,但双重转义了空格 (%2520)...

http://es.metroid.wikia.com/api.php?action=query&list=categorymembers&cmtitle=Categor%C3%ADa:Mejoras%2520de%2520las%2520Botas&cmlimit=500&format=xml

现在,如果我在查询中不使用任何空格,则不会出现双重转义,我会得到所需的输出。因此,如果没有任何非 ASCII 字符的可能性,我就不需要使用 URI 类并且不会进行双重转义。为了避免空间的第一次转义,我尝试了这个:

URI uri = new URI(url, true);
method = new GetMethod(uri.getEscapedURI());
client.executeMethod(method);

但是URI 类不喜欢它:

org.apache.commons.httpclient.URIException: Invalid query
    at org.apache.commons.httpclient.URI.parseUriReference(URI.java:2049)
    at org.apache.commons.httpclient.URI.<init>(URI.java:167)
    at net.metroidover.categorybot.http.HttpRequest.request(HttpRequest.java:66)
    at net.metroidover.categorybot.http.HttpRequest.request(HttpRequest.java:121)
    at net.metroidover.categorybot.http.Action.getCategoryMembers(Action.java:38)
    at net.metroidover.categorybot.bot.BotComponent.<init>(BotComponent.java:58)
    at net.metroidover.categorybot.bot.BotComponent.main(BotComponent.java:80)
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
    at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    at java.util.ArrayList.get(ArrayList.java:322)
    at net.metroidover.categorybot.http.Action.getCategoryMembers(Action.java:39)
    at net.metroidover.categorybot.bot.BotComponent.<init>(BotComponent.java:58)
    at net.metroidover.categorybot.bot.BotComponent.main(BotComponent.java:80)

任何关于如何避免这种双重转义的意见将不胜感激。我一直潜伏在周围,完全没有运气。

谢谢!

编辑: 最适合我的解决方案是 parsifal 的解决方案,但作为补充,我想说使用 method.setPath(url) 设置路径使 HttpMethod 拒绝 cookie我需要保存:

Aug 26, 2011 4:07:08 PM org.apache.commons.httpclient.HttpMethodBase processCookieHeaders
WARNING: Cookie rejected: "wikicities_session=900beded4191ff880e09944c7c0aaf5a". Illegal path attribute "/". Path of origin: "http://es.metroid.wikia.com/api.php"

但是,如果我将 URI 发送给构造函数而忘记了 setPath(url),则 cookie 将毫无问题地保存。

String url = "http://es.metroid.wikia.com/api.php";
NameValuePair[] query = { new NameValuePair("action", "query"), new NameValuePair("list", "categorymembers"),
            new NameValuePair("cmtitle", "Categoría:Mejoras de las Botas"), new NameValuePair("cmlimit", "500"),
            new NameValuePair("format", "xml") };
HttpMethod method = null;

...

method = new GetMethod(url);  // Or PostMethod(url)
method.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); // It had been like this the whole time
method.setQueryString(query);
client.executeMethod(method);

【问题讨论】:

    标签: java url uri httpclient mediawiki-api


    【解决方案1】:

    我建议使用UrlEncoder 对您的 queryString 值(而不是整个 queryString)进行编码。

    UrlEncoder.encode("Categoría:Mejoras de las Botas", "UTF-8");
    

    【讨论】:

    • 这很好用,但是您必须单独编码所有查询参数。我发现 parsifal 的答案更有用,因为所有 NameValuePairs 都使用 method.setQueryString(pairs); 进行编码,即 pairsNameValuePair[]
    【解决方案2】:

    查看HttpMethodBase 的文档,似乎所有String 参数都必须预先编码。最简单的解决方案是分阶段构造您的 URL,使用 setPath()setQueryString() 的变体,它采用一组名称-值参数。

    【讨论】:

    • 耶!这完美无缺。实际上,我已经将参数作为ArrayList&lt;NameValuePair&gt; 发送了,所以我不必更改太多代码。谢谢:)
    【解决方案3】:

    为什么不尝试将参数添加为NameValuePair,这里的问题是,当您转义 URL 时,URL 中的所有内容都被转义,包括 http://.. 这就是系统抱怨的原因。

    您也可以使用URLEncoder.encode() 仅对参数进行转义,只需将获取参数传递给此并将返回值附加到 URL。

    String url = "http://es.metroid.wikia.com/api.php?"+URLEncoder.encode("action=query&amp;list=categorymembers&amp;cmtitle=Categoría:Mejoras de las Botas");

    【讨论】:

    • 我认为每个参数都必须单独编码,否则 & 和 = 将被编码。
    • 是的,就像@JB Nizet 所说,你必须单独编码,否则你会得到http://es.metroid.wikia.com/api.php?action%3Dquery%26list%3Dcategorymembers%26c‌​mtitle%3DCategor%C3%ADa%3AMejoras+de+las+Botas
    猜你喜欢
    • 2014-11-30
    • 2019-09-26
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    • 2014-12-27
    相关资源
    最近更新 更多