Android HttpURLConnection receives HTTP 301 respon

2019-04-15 06:54发布

问题:

I'm trying to do a HTTP GET using the HttpURLConnection object in Android.

UPDATE

I tried connection to a different server. This is also hosted within Cloud 9 (c9.io) and also returns a json response. This time I'm not getting a 301 redirect, but I am getting the actual response the server is supposed to send.

Since this means the problem is localised within the server, I've reorganized the following sections in order to focus reading onto the server-related information. Android related information has been moved to the end of the question.

Where I am connecting:

  • Development server on Cloud9
  • Using the Laravel Framework 5.2 (we cannot upgrade to 5.3 at this time, due to unsupported project dependencies)
  • The server should return a JSON answer
  • If I connect to the same URL through the browser I get the correct response (JSON string. Required HTTP Headers and a '200' HTTP Response Code)

Where I am connecting FROM

  • Android phone (Oneplus 3, on Android 6.0)
  • Compile SDK version: 23
  • Using Build Tools: "23.0.3"
  • Using Min SDK verion: 19
  • Using Target SDK version: 22
  • I'm connectiong using a HttpURLConnection object, using HTTP Method 'GET'

HTTP Response on Android

When I run my code I get the folling result from the server:

The HTTP response code is 301 but the message is null.

  1. The new URL is exactly the same, but using HTTPS. It seems server is somehow forcing SSL/TSL encryption. Which does not happen when accessing HTTP from the browser.

HTTP Header (on Android):

  • date => Tue, 04 Oct 2016 05:56:26 GMT
  • location => https://domain.com/route/ (I modified this bit)
  • content-length => 382
  • content-type => text/html; charset=iso-8859-1
  • X-BACKEND => apps-proxy
  • X-Android-Selected-Protocol => http/1.1
  • X-Android-Sent-Millis => 1475560583894
  • X-Android-Received-Millis => 1475560585637
  • X-Android-Response-Source => NETWORK 301
  • null => HTTP/1.1 301

Other data

  1. Since it seems the server wants Android to use HTTPS, I tried modifying the code to use HTTPS (HttpsURLConnection). This may or may not solve this problem, but I am unable to check it since I get an annoying SSL handshake failed error. Plus I have no need for encryption on this application, and therefore I'm reluctant to solve the problems coming with it.
  2. This is all running within an AsyncTask object (since Android get moody when you try to use a network connection on the main thread).
  3. Setting up a new server (outside of Cloud 9 and without any SSL/TSL) could be an option, but I'm reluctant to do this since it would be quite time consuming.
  4. I tried connecting to another Cloud 9 server (which also returns a json response), using the exact same code, and everything works correctly. This suggests that the problem arises from the HTPP 301 error.

I will try to share with you any other information you may require to answer my question!

Native Android stuff (moved on UPDATE, see above)

The response content seems to be an incomplete JSON:

{ 'status':'ERROR'

Note I did NOT forget the closing } character, that's what the response actually containts. This is injected somewhere unknown (to me) during the workflow. When I capture the HTTP response (using Charles on my PC, which is set as a Proxy for my phone's Wi-Fi connection) it's content is (as expected) a simple HTML telling you to redirect (HTPP code 301) to a new route.

The invalid JSON code (above) isn't there, but a valid HTML is.

This would suggest that the invalid JSON appears somewhere internally to my code (not on the server, or transport). But there is no code on my app that generates a JSON string, let alone inject it into the response I'm processing.

Code for the HttpURLConnection

this.setURL(ruta); //gets correct url
HttpURLConnection cxn = (HttpURLConnection) this.getURL().openConnection(); //init
cxn.setRequestMethod("GET"); //use HTTP GET verb
cxn.setUseCaches(false); //no cache
cxn.setRequestProperty("Cache-Control", "no-cache"); //even less cache
cxn.setDoOutput(false); //only true in POST/PUT requests
cxn.setRequestProperty("Connection","keep-alive");
cxn.setRequestProperty("DNT", "1"); //TEMP
cxn.setInstanceFollowRedirects(true); //should follow redirects
cxn.setRequestProperty( "charset", "utf-8");

Code for the reading the result

int status_code = cxn.getResponseCode();
InputStream responseStream = new BufferedInputStream(cxn.getInputStream());
BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(responseStream));
String line = "";
StringBuilder stringBuilder = new StringBuilder();
while ((line = responseStreamReader.readLine()) != null) {
    stringBuilder.append(line).append("\n");
}
responseStreamReader.close();
String response = stringBuilder.toString();
cxn.disconnect();

回答1:

Remove the code you've used to create the HttpURLConnection and try with this one:

URL url;
HttpURLConnection urlConnection = null;
try {
    url = new URL("http://www.domain.com/index.aspx?parameter1=X&parameter2=X"); //Use your url and add the GET parameters

    urlConnection = (HttpURLConnection) url.openConnection();

    urlConnection.setInstanceFollowRedirects(false); /* added line */

    InputStream in = urlConnection.getInputStream();

    InputStreamReader isw = new InputStreamReader(in);

    int data = isw.read();
    while (data != -1) {
        char current = (char) data;
        data = isw.read();
        System.out.print(current);
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (urlConnection != null) {
        urlConnection.disconnect();
    }    
}

This should be all you need to set for your GET request.

EDIT:

I've tested the webservice using Volley, here's the code I've used in order to retrieve the webservice response:

public class MainActivity extends AppCompatActivity {

  public String response;
  TextView textView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView = (TextView) findViewById(R.id.rTextView);

    RequestQueue queue = Volley.newRequestQueue(this);
    String url = "yourWebserviceUrl";

    // Request a string response from the provided URL. 
    StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
      new Response.Listener < String > () {
        @Override
        public void onResponse(String response) {
          textView.setText("Response is: " + response);
        }
      }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
          textView.setText("That didn't work!");
        }
      });
    // Add the request to the RequestQueue. 
    queue.add(stringRequest);

  }

}

And this is the response I got:

{"status":"ok","found":false,"extra":"App\\Scanners"}


回答2:

Changing the protocol to https worked for me.



回答3:

Change the line : HttpURLConnection cxn = (HttpURLConnection) this.getURL().openConnection();

with : HttpsURLConnection cxn = (HttpsURLConnection) this.getURL().openConnection();

So you will able to handle https