«Давний читатель, первый плакат» здесь.

Я занимаюсь созданием бота для испанской Wiki, которую я администрирую. Я хотел сделать его с нуля, так как одна из целей моего создания - практиковать Java. Однако я столкнулся с некоторыми проблемами при попытке выполнить запросы GET с HttpClient к URI, которые содержат символы, отличные от ASCII, такие как á, é, í, ó или ú.

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, а í остаются как есть. Тот же самый URI отлично работает в браузере, но я не могу заставить GetMethod его принять.

Я также пробовал делать следующее:

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

Таким образом, URI экранировал символы i, но удваивал экранирование пробелов (%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)

Мы будем очень благодарны за любые предложения о том, как избежать этого двойного экранирования. Я скрывался повсюду, но безуспешно.

Благодарность!

Изменить: решение, которое лучше всего подходит для меня, - это решение парсифаля, но в качестве дополнения я хотел бы сказать, что установка пути с помощью 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);
4
ianmartorell 25 Авг 2011 в 21:38

3 ответа

Лучший ответ

Изучите документацию HttpMethodBase , похоже, все параметры String должны быть предварительно закодированы. Самым простым решением является построение URL-адреса поэтапно с использованием setPath() и варианта setQueryString(), который принимает массив параметров значения имени.

2
parsifal 25 Авг 2011 в 17:49

Я бы рекомендовал использовать UrlEncoder для кодирования значений вашей строки запроса (а не всей строки запроса).

UrlEncoder.encode("Categoría:Mejoras de las Botas", "UTF-8");
5
nicholas.hauschild 25 Авг 2011 в 17:50

Почему бы вам не попробовать добавить параметры как NameValuePair, проблема здесь в том, что, когда вы экранируете URL, все в URL экранируются, включая такие вещи, как http: // .. вот почему система жалуется.

Вы также можете избежать только аргументов, используя URLEncoder.encode(), просто передайте ему параметры получения и добавьте возвращаемое значение к URL-адресу.

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

-1
Anantha Sharma 25 Авг 2011 в 17:50