执行请求后,我想检查请求标头,但是它不起作用。

我在getRequestProperties()实例上调用sun.net.www.protocol.http.HttpURLConnection,并且始终收到带有消息“Already connect”的IllegalStateException。好像我想设置请求属性。但是我只想阅读它们。

负责此行为的代码在HttpUrlConnection中:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/sun/net/www/protocol/http/HttpURLConnection.java#HttpURLConnection.getRequestProperties%28%29

public synchronized Map<String, List<String>> getRequestProperties() {
    if (connected)
        throw new IllegalStateException("Already connected");
    // ...
}

好的,也许我应该仅在断开连接后读取请求属性。但事实证明,disconnect()并未将connected设置为false。虽然应该:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/sun/net/www/protocol/http/HttpURLConnection.java#HttpURLConnection.disconnect%28%29

无论我是否读完流,这似乎也没有什么不同。在调用断开连接之前或之后关闭InputStream也不会有任何不同。

我很困惑。你能帮助我吗?
  • 为什么disconnect()设置为false?
  • 为什么在urlConnection连接时不能读取请求属性?
  • 您如何在请求后正确读取请求标头?

  • 重现此代码是针对Android的单元测试(我使用Robolectric),但我认为您也可以在Java项目中使用它,并在删除测试注释后从main()调用它:
    /**
     * Test if HttpUrlConnection works as expected, because in some cases it seems it doesn't
     *
     * @throws Exception
     */
    @Test
    public void testHttpUrlConnection() throws Exception
    {
        final URL url = new URL("http://www.stackoverflow.com");
        final HttpURLConnection urlConnection = ( HttpURLConnection ) url.openConnection( );
        urlConnection.setRequestMethod("GET");
        InputStream is = null;
        try
        {
            is = urlConnection.getInputStream();
            assertEquals(200, urlConnection.getResponseCode());
        }
        catch (IOException ex)
        {
            is = urlConnection.getErrorStream( );
        }
        final String result = copyStreamToString(is); // some html response
        // Streams must be closed before disconnecting (according to http://stackoverflow.com/a/11056207/3596676)
        is.close();
        assertTrue((Boolean) getFieldViaRecursiveReflection(urlConnection, "connected"));
        // urlConnection should always be disconnected (according to http://developer.android.com/reference/java/net/HttpURLConnection.html)
        urlConnection.disconnect();
        assertFalse((Boolean) getFieldViaRecursiveReflection(urlConnection, "connected")); // AssertionError
        // getRequestProperties throws IllegalStateException ("already connected")
        Map<String, List<String>> requestProperties = urlConnection.getRequestProperties();
        // do stuff with the properties
        // return the result
    }
    
    private static String copyStreamToString( final InputStream is ) throws IOException
    {
        if ( is == null )
        {
            return "";
        }
        BufferedReader reader = new BufferedReader( new InputStreamReader( is ) );
        String result = copyBufferedReaderToString( reader );
        reader.close( );
        return result;
    }
    
    private static String copyBufferedReaderToString( final BufferedReader bufferedReader ) throws IOException
    {
        StringBuffer sb = new StringBuffer( );
        String line;
        while ( ( line = bufferedReader.readLine( ) ) != null )
        {
            sb.append( line );
        }
        return sb.toString( );
    }
    
    private static Object getFieldViaRecursiveReflection(final Object object, final String attribute) throws Exception
    {
        return getFieldViaRecursiveReflection(object, object.getClass(), attribute);
    }
    
    private static Object getFieldViaRecursiveReflection(final Object object, final Class<?> c, final String attribute) throws Exception
    {
        try
        {
            final Field field = c.getDeclaredField(attribute);
            field.setAccessible(true);
            return field.get(object);
        }
        catch (NoSuchFieldException ex)
        {
            /* end of type hierarchy? */
            Class<?> superClass = c.getSuperclass();
            if (superClass == null)
            {
                throw ex;
            }
            else
            {
                return getFieldViaRecursiveReflection(object, superClass, attribute);
            }
        }
    }
    

    最佳答案

    自从我提出问题以来的两个月内,没有人发布答案,但是今天我不得不再次解决该问题并找到解决方案,我将回答我自己的问题。

    我无法回答答案中的所有问题(例如“为什么urlConnection.disconnect()不将connectedurlConnection属性设置为false吗?”),但我找到了主要问题的解决方案,即读取请求的标头连接urlConnection时不起作用。

    由于某种原因(我不记得了),我希望/需要在请求完成并且响应在那里之后检查请求标头。但是我再次查看了getRequestProperties()sun.net.www.protocol.http.HttpURLConnection的实现(请参见代码here),并注意到调用了一种称为filterAndAddHeaders的方法。因此,似乎不仅可以在该方法中读取标头,还可以对其进行设置。我不确定为什么要使用getter方法(即getRequestProperties()一个)完成此操作,但是有意义的是,当请求已完成时,当用户尝试添加请求标头时应警告用户-在这种情况下使用IllegalStateException令我非常困扰。

    得出解决方案:

    在发送请求之前,我只是将对getRequestProperties()的调用移至。现在一切正常。

    附言:

    请注意,这还不是全部。即使我在请求后调用getRequestProperties(),我的单元测试之一仍成功运行。在这种情况下,urlConnection内部属性connected设置为false。我还没有弄清楚,但是它可能与响应状态代码304(未修改)有关。如果您必须处理此问题,并且由于某种原因在发送请求之前无法将getRequestProperties()调用移至,这可能会有所提示。

    08-04 09:22