最近发现一个有意思的bug:
明明已经设置了http请求的方式是get,但是实际请求时自动变成了post
debug过程如下:
1、我们先看一下出问题的http设置代码:
public static void loadToken(Context context, checkTokenFinishListener listener) throws Exception {
HttpURLConnection connection = null;
try {
URL url = new URL(getLoadTokenUrl());
connection = (HttpURLConnection) url.openConnection();
HttpsURLConnection.setFollowRedirects(false);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setConnectTimeout(CYFileUploaderByQiniu.getConnTimeout());
connection.setReadTimeout(CYFileUploaderByQiniu.getReadTimeout());
//这里明确地设置了http的RequestMethod是GET方式
connection.setRequestMethod("GET");
connection.connect();
//处理请求结果
......
可以看到很明显的,设置了这个http请求的方式为GET。那为什么设置了还是会变成post呢?
打断点发现只要connect()执行了以后就会自动变成post,说明在connect()里有修改请求方式的处理。
进去发现是个URLConnection的抽象方法,如图:
说明肯定是在某个继承了URLConnection的子类里实现了connect()的逻辑,我们现在需要找到是哪个子类实现了这个connect()方法。
2、首先回去看一下最上面出问题的代码,发现使用了HttpURLConnection这个类。
但是它虽然继承了URLConnection,但是也没实现connect(),猜测是:
某个HttpURLConnection的子类(实现类) 实现的该方法。
3、那怎么看是哪个HttpURLConnection实现类呢
?
看最上面的代码,发现是通过url.openConnection()获取的。
connection = (HttpURLConnection) url.openConnection();
我们进入openConnection()方法看一下,发现是URL类。
它的openConnection()方法代码如下:
public URLConnection openConnection() throws IOException {
return streamHandler.openConnection(this);
}
发现这个openConnection这个方法调用了一个streamHandler类的openConnection方法。
点击进streamHandler的openConnection方法发现又是个抽象方法,如图:
4、现在,需要找到streamHandler.openConnection时使用了哪个streamHandler子类(实现类)
。
在URL类里搜索,发现是在初始化时,设置了个默认的streamHandler类,代码如图:
具体的设置在setupStreamHandler()这个函数里。
点击进去,看看函数写了什么。
在函数底部发现了我们要找的那个streamHandler实现类
:
在http情况下用的是com.android.okhttp.HttpHandler
这个类,
代码如图:
5、好,现在我们找到了第4步里要找的streamHandler实现类
这个类就是com.android.okhttp.HttpHandler。
但是这个类在项目里是看不了的,得去android源码库里看。
我们去android源码库androidxref,点击查看,选择一个android版本。
我用的测试机是5.1.0的,选择后如图:
在第二行Definition里输入HttpHandler,
在右侧的搜索范围(In Project)里选择select all ,
然后点击底部的search按钮,查看结果,如图:
第一个结果就是我们要找的okHttp的HttpHandler类,点击进去看看代码。
发现HttpHandler的openConnection()方法里调用的是newOkHttpClient(proxy)的open(url)方法,如图:
点击newOkHttpClient类,再看看open方法干了什么,如图:
发现这个open方法,返回了个HttpURLConnectionImpl实例。
6、终于,找到了第3步里要找的HttpURLConnection子类
快看看有没有connect()的具体实现。
http自动修改请求方法的秘密就在那里!
果然,有connect()的具体实现,里面有个设置http的方法
initHttpEngine()
点击进去看到http的设置代码逻辑,如图:
7、原来connect()时会判断是否设置了doOutput为true。如果设置为true,会自动将get方式改为post方式!!
8、回到最开始有问题的代码,看看是不是设置了doOutput?
如图:
果然设置为true了!答案找到了!