Android volley to handle redirect
Asked Answered
F

8

36

I recently started to use Volley lib from Google for my network requests. One of my requests get error 301 for redirect, so my question is that can volley handle redirect somehow automatically or do I have to handle it manually in parseNetworkError or use some kind of RetryPolicyhere?

Thanks.

Foin answered 5/7, 2013 at 5:55 Comment(2)
In case you're interested in the opposite case (don't want volley to handle redirects automatically) check https://mcmap.net/q/14515/-change-redirect-policy-of-volley-frameworkEspinal
Thanks @DenisKniazhev! That is exactly what I was googling :DNicolnicola
F
21

I fixed it catching the http status 301 or 302, reading redirect url and setting it to request then throwing expection which triggers retry.

Edit: Here are the main keys in volley lib which i modified:

  • Added method public void setUrl(final String url) for class Request

  • In class BasicNetwork is added check for redirection after // Handle cache validation, if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) || statusCode == HttpStatus.SC_MOVED_TEMPORARILY), there I read the redirect url with responseHeaders.get("location"), call setUrl with request object and throw error

  • Error get's catched and it calls attemptRetryOnException

  • You also need to have RetryPolicy set for the Request (see DefaultRetryPolicy for this)

Foin answered 5/7, 2013 at 7:7 Comment(5)
Did you modify the Volley source code for this or does the API support it already? Please share code examples for other people running into the same problemMincey
I edited my answer to show they key points for the redirection.Foin
Please see github.com/samkirton/android-volley for a fork of volley with the redirect fix.Bankrupt
Thank you very much for your explanations and your source code because I didn't found anything else working. My problem was with the NetworkImageView and your answer solves everythingBialystok
responseHeaders.get("Location") not responseHeaders.get("location")Unmannered
P
35

Replace your url like that url.replace("http", "https");

for example: if your url looking like that : "http://graph.facebook......." than it should be like : "https://graph.facebook......."

it works for me

Pasadis answered 19/12, 2014 at 13:1 Comment(3)
Non-english speaking in stackoverflow is not good. But "Kime ne amk?".Stiver
haha... That worked like a charm ! I have experienced a similar issue with HttpURLConnection in another project. The same fix worked for that as well.Pistil
This is off subject. The server might not even offer SSL. This is a one time specualtion, that happened to work.Percyperdido
F
21

I fixed it catching the http status 301 or 302, reading redirect url and setting it to request then throwing expection which triggers retry.

Edit: Here are the main keys in volley lib which i modified:

  • Added method public void setUrl(final String url) for class Request

  • In class BasicNetwork is added check for redirection after // Handle cache validation, if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) || statusCode == HttpStatus.SC_MOVED_TEMPORARILY), there I read the redirect url with responseHeaders.get("location"), call setUrl with request object and throw error

  • Error get's catched and it calls attemptRetryOnException

  • You also need to have RetryPolicy set for the Request (see DefaultRetryPolicy for this)

Foin answered 5/7, 2013 at 7:7 Comment(5)
Did you modify the Volley source code for this or does the API support it already? Please share code examples for other people running into the same problemMincey
I edited my answer to show they key points for the redirection.Foin
Please see github.com/samkirton/android-volley for a fork of volley with the redirect fix.Bankrupt
Thank you very much for your explanations and your source code because I didn't found anything else working. My problem was with the NetworkImageView and your answer solves everythingBialystok
responseHeaders.get("Location") not responseHeaders.get("location")Unmannered
H
20

If you dont want to modify the Volley lib you can catch the 301 and manually re-send the request.

In your GsonRequest class implement deliverError and create a new Request object with the new Location url from the header and insert that to the request queue.

Something like this:

@Override
public void deliverError(final VolleyError error) {
    Log.d(TAG, "deliverError");

    final int status = error.networkResponse.statusCode;
    // Handle 30x 
    if(HttpURLConnection.HTTP_MOVED_PERM == status || status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_SEE_OTHER) {
        final String location = error.networkResponse.headers.get("Location");
        Log.d(TAG, "Location: " + location);
        final GsonRequest<T> request = new GsonRequest<T>(method, location, jsonRequest, this.requestContentType, this.clazz, this.ttl, this.listener, this.errorListener);
        // Construct a request clone and change the url to redirect location.
        RequestManager.getRequestQueue().add(request);
    }
}

This way you can keep updating Volley and not have to worry about things breaking.

Housewares answered 11/2, 2015 at 12:25 Comment(1)
Thanks, You save my time.Cosine
N
7

Like many others, I was simply confused about why Volley wasn't following redirects automatically. By looking at the source code I found that while Volley will set the redirect URL correctly on its own, it won't actually follow it unless the request's retry policy specifies to "retry" at least once. Inexplicably, the default retry policy sets maxNumRetries to 0. So the fix is to set a retry policy with 1 retry (10s timeout and 1x back-off copied from default):

request.setRetryPolicy(new DefaultRetryPolicy(10000, 1, 1.0f))

For reference, here is the source code:

/**
 * Constructs a new retry policy.
 * @param initialTimeoutMs The initial timeout for the policy.
 * @param maxNumRetries The maximum number of retries.
 * @param backoffMultiplier Backoff multiplier for the policy.
 */
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
    mCurrentTimeoutMs = initialTimeoutMs;
    mMaxNumRetries = maxNumRetries;
    mBackoffMultiplier = backoffMultiplier;
}

Alternatively, you can create a custom implementation of RetryPolicy that only "retries" in the event of a 301 or 302.

Hope this helps someone!

Nonaggression answered 29/2, 2016 at 23:23 Comment(3)
FYI: The default retry policy was set to 0 to "prevent some potential problems": github.com/mcxiaoke/android-volley/commit/…Ambrose
FYI FYI, another possible clue to redirects not working is this "recent" (2015/12/04) fix in the official upstream repo, that apparently has not made it in to the unofficial downstream github mirror repo: android.googlesource.com/platform/frameworks/volley/+/…Ambrose
With all respect and no downvote, please note that request.setRetryPolicy(new DefaultRetryPolicy(10000, 1, 1.0f)); doesnt magically handle redirects. Creating a custom implementation is half way towards solving the issue, and no error handling was shown in that case.Litalitany
Y
5

End up doing a merge of what most @niko and @slott answered:

// Request impl class
// ...

    @Override
    public void deliverError(VolleyError error) {
        super.deliverError(error);

        Log.e(TAG, error.getMessage(), error);

        final int status = error.networkResponse.statusCode;
        // Handle 30x
        if (status == HttpURLConnection.HTTP_MOVED_PERM ||
                status == HttpURLConnection.HTTP_MOVED_TEMP ||
                status == HttpURLConnection.HTTP_SEE_OTHER) {
            final String location = error.networkResponse.headers.get("Location");
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "Location: " + location);
            }
            // TODO: create new request with new location
            // TODO: enqueue new request
        }
    }

    @Override
    public String getUrl() {
        String url = super.getUrl();

        if (!url.startsWith("http://") && !url.startsWith("https://")) {
            url = "http://" + url; // use http by default
        }

        return url;
    }

It worked well overriding StringRequest methods.

Hope it can help someone.

Yost answered 31/8, 2017 at 4:35 Comment(0)
C
4

Volley supports redirection without any patches, no need for a separate fork

Explanation: Volley internally uses HttpClient which by default follows 301/302 unless specified otherwise

From: http://hc.apache.org/httpcomponents-client-4.2.x/tutorial/html/httpagent.html

ClientPNames.HANDLE_REDIRECTS='http.protocol.handle-redirects': defines whether redirects should be handled automatically. This parameter expects a value of type java.lang.Boolean. If this parameter is not set HttpClient will handle redirects automatically.

Com answered 17/4, 2014 at 18:25 Comment(4)
For me its handling some automatically but some its reported unexpected response 302 and failing.Bancroft
'Volley internally uses HttpClient' - that's not accurate, it uses HttpClient only only with API < 9 and uses HTTPUrlConnection otherwise (which got replaced with Okhttp's impl in kit-kat)Espinal
I'm on Lollipop and latest Volley and I'm getting "BasicNetwork.performRequest: Unexpected response code 301 for ...". So something is not working. I can handle this in Request.deliverError but I'd rather that it was transparent.Housewares
The HttpURLConnection‘s follow redirect is just an indicator, in fact it won’t help you to do the “real” http redirection, you still need to handle it manually.Housewares
A
2

ok, im a bit late to the game here, but i've recently been trying to achieve this same aspect, so https://mcmap.net/q/418702/-android-volley-to-handle-redirect is the best one, given that you are willing to fork volley and maintain it and the answer here : https://mcmap.net/q/418702/-android-volley-to-handle-redirect - I'm not sure how this even worked.This one is spot on though : https://mcmap.net/q/418702/-android-volley-to-handle-redirect. But its actually adding a new request object to the NetworkDipatcher's queue, so you'll have to notify the caller as well somehow, there is one dirty way where you can do this by not modifying the request object + changing the field "mURL", PLEASE NOTE THAT THIS IS DEPENDENT ON YOUR IMPLEMENTATION OF VOLLEY'S RetryPolicy.java INTERFACE AND HOW YOUR CLASSES EXTENDING Request.java CLASS ARE, here you go : welcome REFLECTION

Class volleyRequestClass = request.getClass().getSuperclass();
                        Field urlField = volleyRequestClass.getDeclaredField("mUrl");
                        urlField.setAccessible(true);
                        urlField.set(request, newRedirectURL);

Personally I'd prefer cloning volley though. Plus looks like volley's example BasicNetwork class was designed to fail at redirects : https://github.com/google/volley/blob/ddfb86659df59e7293df9277da216d73c34aa800/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java#L156 so i guess they arent leaning too much on redirects, feel free to suggest/edit. Always looking for good way..

Ad answered 19/3, 2017 at 1:6 Comment(0)
G
0

I am using volley:1.1.1 with https url though the request was having some issue. On digging deeper i found that my request method was getting changed from POST to GET due to redirect (permanent redirect 301). I am using using nginx and in server block i was having a rewrite rule that was causing the issue.

So in short everything seems good with latest version of volley. My utility function here-

public void makePostRequest(String url, JSONObject body, final AjaxCallback ajaxCallback) {
    try {
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,
                url, body, new Response.Listener<JSONObject>() {

            @Override
            public void onResponse(JSONObject response) {
                Log.d(LOG, response.toString());
                ajaxCallback.onSuccess(response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(LOG, error.toString());
                ajaxCallback.onError(error);
            }
        });
        singleton.getRequestQueue().add(jsonObjectRequest);
    } catch(Exception e) {
        Log.d(LOG, "Exception makePostRequest");
        e.printStackTrace();
    }
}

// separate file
public interface AjaxCallback {
    void onSuccess(JSONObject response);
    void onError(VolleyError error);
}
Gaddi answered 31/7, 2018 at 15:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.