雷达智富

首页 > 内容 > 程序笔记 > 正文

程序笔记

Android Volley BasicNetwork:performRequest:Unexpected response code 302 错误

2024-09-23 29

Android Volley BasicNetwork:performRequest:Unexpected response code 302 for http://xxx异常,这个http://xxx的接口在浏览器中打开可以正常访问。

接口做了302重定向,从http重定向到https的地址去了。

Volley对重定向并没有额外处理,我们可以看https://github.com/mcxiaoke/android-volley的代码对重定向的处理,处理详情见BasicNetwork类。

在该类里,当程序发现响应返回的状态码是301或302时,则改动原请求的请求URL为重定向URL,然后抛出异常。

if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
      String newUrl = responseHeaders.get("Location");
        request.setRedirectUrl(newUrl);//原请求指定重定向的URL
 }

由于Volley有重试机制,对于响应失败的请求,会根据重试策略再次进行请求,此时,在while(true)里重新执行上次返回302的请求,则完成了对重定向的处理。

异常捕获的时候,对重定向状态单独处理,代码如下:

else if(statusCode==HttpStatus.SC_MOVED_PERMANENTLY||
    statusCode==HttpStatus.SC_MOVED_TEMPORARILY) {
    attemptRetryOnException("redirect",//attemptRetryOnException方法见前述
    request,newRedirectError(networkResponse));//返回302则抛出重定向异常
}

但是这种对重定向的解决方案并不能满足一些场景,比如:

1、原请求是POST请求,但是重定向一般是GET请求,这样只改变原请求的访问地址,并不一定能请求成功。

2、重定向请求需要带的头信息与原请求不一致的,比如重定向是跨域的,可能需要对cookie处理。

除此之外,这种解决方案需要指定重试策略,但Volley默认是不重试的,而且我认为,大多数场景下,系统并不需要自动重试,而是由用户自己重新发起请求。这样一来,为了重定向需要单独指定重试策略,对代码的统一处理略有不便。

既然这种方案不能满足需求,那就需要改动源码了。

重定向的处理应该在访问请求的最底层处理比较合适,可以在HurlStack里处理。

自己处理重定向需要对connection加句代码:

connection.setInstanceFollowRedirects(false)

处理重定向的时候需要满足递归性,也就是说,A地址重定向到B地址,B地址又重定向到C地址,无限重定向,要能持续处理,并且原路返回结果。

HurlStack处理请求的方法是

public HttpResponse performRequest(Request request,Map additionalHeaders)

这里的入参是request对象,改动原request对象重新请求是比较麻烦的,而且由于是子线程里运行的,容易出现很多难解的问题。

理想的方式是构造新的请求,但是不能是构造新的Request对象,因为我们还要通过入参的request对象原路返回结果。

这时需要将performRequest方法里执行请求的代码提炼出一个方法来

private HttpResponse executeRequest(int method,String url,Map headers, byte[] postBody,
int timeoutMs,String contentType)

并且对重定向的处理如下:

if(responseStatus.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY||
          responseStatus.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) {
  String redirectUrl = connection.getHeaderField("Location");
  if(redirectUrl !=null&& redirectUrl.length() >0) {
    return executeRequest(Method.GET,redirectUrl,headers, null,timeoutMs,contentType);
  }
}

相应的,本类的一些其他的被调用方法由于入参类型问题也需要相应的变动,不过整体来说,改动还是很简单的。

另外可以增加两个方法onPrepareRequest、onReponseFinished;onPrepareRequest方法在请求调用之前执行,onReponseFinished可以在请求调用之后,重定向之前执行。空实现即可,需要的时候,在子类里处理即可,可以用于打印日志或者处理头信息等。

虽然改动量相对来说,略多,但是对一些场景来说,还是需要的。当然,如果重试机制的处理方式即可满足需求,那就无需动源码了。

更新于:3个月前
赞一波!

文章评论

评论问答