Получить окончательное расположение заданного URL-адреса в Java

Я пытаюсь получить окончательное расположение заданного URL-адреса (String ref) следующим образом:

        HttpURLConnection con = (HttpURLConnection)new URL(ref).openConnection();
        con.setInstanceFollowRedirects(true);
        con.setRequestProperty("User-Agent","");
        int responseCode = con.getResponseCode();
        return con.getURL().toString();

Он работает в большинстве случаев, но редко возвращает URL-адрес, который все же содержит другое перенаправление.

Что я здесь делаю не так?

Почему я получаю responseCode = 3xx даже после вызова setInstanceFollowRedirects (true)?

ОБНОВЛЕНИЕ:

Хорошо, responseCode иногда может быть 3xx.

Если это произойдет, я верну вместо него con.getHeaderField ("Location").

Теперь код:

        HttpURLConnection con = (HttpURLConnection)new URL(ref).openConnection();
        con.setInstanceFollowRedirects(true);
        con.setRequestProperty("User-Agent","");
        int responseType = con.getResponseCode()/100;
        while (responseType == 1)
        {
            Thread.sleep(10);
            responseType = con.getResponseCode()/100;
        }
        if (responseType == 3)
            return con.getHeaderField("Location");
        return con.getURL().toString();

Буду признателен за комментарий, если кто-нибудь увидит что-то не так с приведенным выше кодом.

ОБНОВЛЕНИЕ

  • Удалена обработка кода 1xx, поскольку, по мнению большинства комментаторов, в этом нет необходимости.
  • Проверка наличия заголовка Location перед его возвратом для обработки кода 304.

        HttpURLConnection con = (HttpURLConnection)new URL(ref).openConnection();
        con.setInstanceFollowRedirects(true);
        con.setRequestProperty("User-Agent","");
        if (con.getResponseCode()/100 == 3)
        {
            String target = con.getHeaderField("Location");
            if (target != null)
                return target;
        }
        return con.getURL().toString();
    

person barak manos    schedule 27.12.2013    source источник
comment
Он не будет следовать перенаправлению для ответа, который возвращает 30x, но не имеет заголовка ответа Location.   -  person Mike Samuel    schedule 28.12.2013
comment
Разве в ответах 3xx не всегда должен быть заголовок Location? Я все еще озадачен тем фактом, что я получаю 3xx в первую очередь (после установки InstanceFollowRedirects = true), но я решил, что если возвращается ответ 3xx, то, по крайней мере, я могу рассчитывать на то, что он также содержит заголовок Location ... Это неправильное предположение?   -  person barak manos    schedule 28.12.2013
comment
кстати, вы не забыли вызвать con.connect () в этих фрагментах?   -  person Jakub Kotowski    schedule 28.12.2013
comment
@jkbkot нет, он подключается автоматически, когда вы проверяете код ответа или получаете входной поток   -  person aditsu quit because SE is EVIL    schedule 28.12.2013
comment
@barakmanos, нет, у 304 запросов почти никогда не бывает Location заголовков ответов и RFC 2616 говорит, что НЕ ДОЛЖЕН для большинства остальных относительно Location. Обратите внимание, что он также рекомендует, чтобы пользовательские агенты не выполняли более 5 шагов перенаправления и прекращали перенаправление при обнаружении цикла.   -  person Mike Samuel    schedule 28.12.2013
comment
@MikeSamuel, спасибо. Означает ли это, что con.getHeaderField (Location) вернет пустую строку или ноль? Каким будет лучшее решение в этом случае - вернуть con.getURL (). ToString ()?   -  person barak manos    schedule 28.12.2013
comment
@barakmanos, null указывает на отсутствующий заголовок, а пустая строка указывает на недопустимое перенаправление на пустой URL-адрес. Я не знаю, что мне делать лучше всего; Я думаю, это зависит от того, зачем вы это делаете.   -  person Mike Samuel    schedule 28.12.2013


Ответы (5)


HttpURLConnection не будет следовать перенаправлениям при изменении протокола, например с http на https или https на http. В этом случае он вернет код 3xx, и вы сможете получить заголовок Location. Возможно, вам придется снова открыть соединение, если новый URL-адрес также перенаправляет. По сути, используйте цикл и прерывайте его, когда вы получаете код ответа без перенаправления. Кроме того, следите за бесконечными циклами перенаправления, вы можете установить ограничение на количество итераций или проверить, был ли уже посещен каждый новый URL-адрес.

person aditsu quit because SE is EVIL    schedule 27.12.2013
comment
Итак, при получении 3xx должен ли я возвращать myFunc (con.getHeaderField (Location)) вместо просто con.getHeaderField (Location), как описано выше? - person barak manos; 28.12.2013
comment
Это один из способов сделать это, но, как я уже сказал, вам нужно остерегаться петель перенаправления. Думаю, было бы удобнее использовать цикл while. - person aditsu quit because SE is EVIL; 28.12.2013
comment
Кроме того, не уверен, что должен достичь этот спящий цикл, код ответа не изменится. - person aditsu quit because SE is EVIL; 28.12.2013
comment
Итак, что мне делать в случае 1xx? Разве это не означает, что соединение еще не завершено (следовательно, дополнительные вызовы getResponseCode в конечном итоге вернут новый код ответа)? - person barak manos; 28.12.2013
comment
Я видел только 1xx в некоторых случаях при выполнении запросов POST. Здесь вы делаете ПОЛУЧИТЬ. Вы когда-нибудь получали такой код ответа? Если нет, то не беспокойтесь об этом. - person aditsu quit because SE is EVIL; 28.12.2013
comment
Здорово. Спасибо за совет. Кстати, мне нужно явно установить метод запроса на GET (путем вызова setRequestMethod)? - person barak manos; 28.12.2013

Если вам просто нужен URL-адрес перенаправления, заголовок ответа должен дать вам следующее:

if (con.getResponseCode() == 301) {
    String redirectUrl = con.getHeaderField("Location");
}
person evanwong    schedule 27.12.2013
comment
Мне нужно окончательное местоположение, то есть строку URL-адреса после завершения всех перенаправлений. Поэтому после вызова setInstanceFollowRedirects (true) я ожидал, что код ответа будет чем-то другим, кроме 3xx. - person barak manos; 28.12.2013

Вероятно, легко может быть несколько уровней перенаправления - представьте, что вы немного указываете на адрес youtu.be, указывающий на youtube.com. Возможно, вам нужно повторять цикл до тех пор, пока вы не получите 200 OK или пока вы не достигнете цикла перенаправления.

Мне трудно найти исходный код для проверки, но я верю, что то, что я сказал, правда. См., Например, java urlconnection получить окончательный перенаправленный URL

Вам также может потребоваться обработать перенаправления протокола, например HTTP -> HTTPS: URLConnection не следует за перенаправлением

person Jakub Kotowski    schedule 27.12.2013
comment
Разве не для этого предназначен setInstanceFollowRedirects (true)? Чтобы избавить пользователя от беспокойства о множественных перенаправлениях и получить код ответа после их завершения? Я уже пробовал ваше предложение в сочетании с setInstanceFollowRedirects (false). Но в некоторых случаях это решение давало неверный результат (не конечный URL). - person barak manos; 28.12.2013

Думаю, теперь я понимаю, чего вы хотите. Теперь я думаю, что вы пытаетесь получить окончательный адрес, а не его содержимое. Пожалуйста, поправьте меня, если мое предположение неверно.

Для этого (не по содержанию, а по адресу) нужен другой подход. Вам нужно отключить Follow-Redirects, а затем вам нужно самостоятельно обработать итерационное redirect-follow, пока вы не найдете ответ без перенаправления. Имейте в виду, что вы не можете повторно использовать URLConnection.

Подходы к нахождению конечного адреса и другой подход к получению содержимого конечного адреса настолько различаются, потому что URLConnection не показывает адрес, на который осуществляется переход, если вы включаете последующие перенаправления.

В вашем коде вы, кажется, ожидаете, что URLConnection.getURL() вернет адрес, на который вы следуете. Это не поведение этого метода. Он возвращает исходный URL, который вы использовали для создания URLConnection. Это происходит независимо от того, включаете ли вы отслеживаемую переадресацию или нет.
Однако, если вы включите ее, вы не сможете получить URL-адрес, на который выполняется отслеживание. Это связано с тем, что getHeaderField("Location") с последующими перенаправлениями не имеет смысла: он возвращает цель перенаправления окончательного перенаправления, которая не должна существовать, поскольку это окончательный адрес.

person Daniel S.    schedule 29.12.2013

Иногда он загружается в поле requestURI. Используйте как этот код:

val declaredField = con.javaClass.getDeclaredField("requestURI")
declaredField.isAccessible=true
val loc = declaredField.get(con).toString()
person utrucceh    schedule 03.05.2020