2015-05-05 14 views
6

के साथ पुराने अनुरोध को पुनः प्रयास करने से पहले पुनः प्रयास करने पर नया टोकन प्राप्त करना मेरे पास वॉली का उपयोग करके लागू एक सरल प्रमाणीकरण प्रणाली है। यह इस प्रकार है: लॉगिन पर सर्वर से टोकन प्राप्त करें -> एक घंटे बाद, यह टोकन समाप्त हो जाता है -> जब यह समाप्त हो जाता है, तो हम उसे एक असफल एपीआई कॉल पर पाएंगे, इसलिए हमें (पुनः प्रयास करने) -> लाने चाहिए जब नया कॉल विफल रहता है तो नया टोकन और फिर -> मूल कॉल को पुनः प्रयास करें।वॉली

मैंने इसे कार्यान्वित किया है, और टोकन सफलतापूर्वक लौट रहा है, लेकिन क्योंकि मुझे लगता है कि मैं वॉली RequestQueue के साथ कुछ गलत कर रहा हूं, मूल अनुरोध नए और वैध टोकन का उपयोग करने में सक्षम होने से पहले इसके सभी पुनः प्रयासों का उपयोग करता है । कृपया निम्नलिखित कोड देखें:

public class GeneralAPICall extends Request<JSONObject> { 
public static String LOG_TAG = GeneralAPICall.class.getSimpleName(); 

SessionManager sessionManager; //instance of sessionManager needed to get user's credentials 
private Response.Listener<JSONObject> listener; //the response listener used to deliver the response 
private Map<String, String> headers = new HashMap<>(); //the headers used to authenticate 
private Map<String, String> params; //the params to pass with API call, can be null 

public GeneralAPICall(int method, String url, Map<String, String> params, Context context, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) { 
    super(method, url, errorListener); 
    sessionManager = new SessionManager(context); //instantiate 
    HashMap<String, String> credentials = sessionManager.getUserDetails(); //get the user's credentials for authentication 
    this.listener = responseListener; 
    this.params = params; 
    //encode the user's username and token 
    String loginEncoded = new String(Base64.encode((credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) 
      + Constants.APIConstants.Characters.CHAR_COLON 
      + credentials.get(Constants.SessionManagerConstants.KEY_TOKEN)).getBytes(), Base64.NO_WRAP)); 
    Log.v(LOG_TAG, loginEncoded); //TODO: remove 
    this.headers.put(Constants.APIConstants.BasicAuth.AUTHORIZATION, Constants.APIConstants.BasicAuth.BASIC + loginEncoded); //set the encoded information as the header 
    setRetryPolicy(new TokenRetryPolicy(context)); //**THE RETRY POLICY** 
} 

पुन: प्रयास करें नीति मैं सेट डिफ़ॉल्ट के रूप में परिभाषित किया गया है, लेकिन मैं इस तरह के रूप में अपने स्वयं के पुन: प्रयास करें विधि को लागू:

@Override 
public void retry(VolleyError error) throws VolleyError { 
    Log.v(LOG_TAG, "Initiating a retry"); 
    mCurrentRetryCount++; //increment our retry count 
    mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); 
    if (error instanceof AuthFailureError) { //we got a 401, and need a new token 
     Log.v(LOG_TAG, "AuthFailureError found!"); 
     VolleyUser.refreshTokenTask(context, this); //**GET A NEW TOKEN** 
    } 
    if (!hasAttemptRemaining()) { 
     Log.v(LOG_TAG, "No attempt remaining, ERROR"); 
     throw error; 
    } 
} 

ताज़ा टोकन काम के लिए एक RefreshAPICall

को परिभाषित करता है
public static void refreshTokenTask(Context context, IRefreshTokenReturn listener) { 
    Log.v(LOG_TAG, "refresh token task called"); 
    final IRefreshTokenReturn callBack = listener; 

    RefreshAPICall request = new RefreshAPICall(Request.Method.GET, Constants.APIConstants.URL.GET_TOKEN_URL, context, new Response.Listener<JSONObject>() { 

     @Override 
     public void onResponse(JSONObject response) { 
      try { 
       String token = response.getString(Constants.APIConstants.Returns.RETURN_TOKEN); 
       Log.v(LOG_TAG, "Token from return is: " + token); 
       callBack.onTokenRefreshComplete(token); 
      } catch (JSONException e) { 
       callBack.onTokenRefreshComplete(null); //TODO: log this 
       e.printStackTrace(); 
      } 
     } 
    }, new Response.ErrorListener() { 
     @Override 
     public void onErrorResponse(VolleyError error) { 
      Log.v(LOG_TAG, "Error with RETRY : " + error.toString()); 
     } 
    }); 

    VolleySingleton.getInstance(context).addToRequestQueue(request); 
} 

हमारे RefreshAPICall परिभाषा:

public RefreshAPICall(int method, String url, Context context, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) { 
    super(method, url, errorListener); 
    sessionManager = new SessionManager(context); //instantiate 
    HashMap<String, String> credentials = sessionManager.getRefreshUserDetails(); //get the user's credentials for authentication 
    this.listener = responseListener; 
    //encode the user's username and token 
    String loginEncoded = new String(Base64.encode((credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) 
      + Constants.APIConstants.Characters.CHAR_COLON 
      + credentials.get(Constants.SessionManagerConstants.KEY_PASSWORD)).getBytes(), Base64.NO_WRAP)); 
    this.headers.put(Constants.APIConstants.BasicAuth.AUTHORIZATION, Constants.APIConstants.BasicAuth.BASIC + loginEncoded); //set the encoded information as the header 
    setTag(Constants.VolleyConstants.RETRY_TAG); //mark the retry calls with a tag so we can delete any others once we get a new token 
    setPriority(Priority.IMMEDIATE); //set priority as immediate because this needs to be done before anything else 

    //debug lines 
    Log.v(LOG_TAG, "RefreshAPICall made with " + credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) + " " + 
      credentials.get(Constants.SessionManagerConstants.KEY_PASSWORD)); 
    Log.v(LOG_TAG, "Priority set on refresh call is " + getPriority()); 
    Log.v(LOG_TAG, "Tag for Call is " + getTag()); 
} 

मैंने इस अनुरोध की प्राथमिकता को उच्च के रूप में सेट किया है ताकि यह असफल होने से पहले ट्रिगर हो जाए, इसलिए एक बार जब हम टोकन प्राप्त करते हैं तो मूल कॉल वैध टोकन के साथ आग लग सकता है।

अंत में, प्रतिक्रिया पर मैं फिर से प्रयास करें टैग के साथ किसी भी अन्य कार्यों (मामले में एक से अधिक API कॉल विफल रही है और कई पुन: प्रयास करें कॉल किए, हम नए टोकन कई बार अधिलेखित करने के लिए नहीं करना चाहते हैं) को हटा

@Override 
public void onTokenRefreshComplete(String token) { 
    VolleySingleton.getInstance(context).getRequestQueue().cancelAll(Constants.VolleyConstants.RETRY_TAG); 
    Log.v(LOG_TAG, "Cancelled all retry calls"); 
    SessionManager sessionManager = new SessionManager(context); 
    sessionManager.setStoredToken(token); 
    Log.v(LOG_TAG, "Logged new token"); 
} 

दुर्भाग्यवश, लॉगकैट मुझे दिखा रहा है कि टोकन का उपयोग करने से पहले सभी रीट्री हो रही हैं। टोकन सफलतापूर्वक वापस आ रहा है, लेकिन यह स्पष्ट है कि तत्काल प्राथमिकता उस क्रम पर कोई प्रभाव नहीं डाल रही है कि कतार कॉल को प्रेषित करती है।

अन्य कार्यों की सराहना करने से पहले मेरे रिफ्रेशैपिकॉल को कैसे सुनिश्चित किया जाए, इस पर कोई मदद की गई है। मैं सोच रहा हूं कि क्या वॉली मूल विफल कार्य के उप-कार्य के रूप में रीफ्रेशैपिकॉल को मानता है, और इसलिए यह उस मूल कार्य को अपने पुनः प्रयासों के लिए कॉल करने का प्रयास करता है जब तक कि वे बाहर नहीं होते हैं, और फिर ताज़ा करें।

LogCat (यकीन नहीं इस सुंदर लग रही बनाने के लिए कैसे):

05-05 16:12:07.145: E/Volley(1972): [137] BasicNetwork.performRequest: 
Unexpected response code **401 for https://url.me/api/get_friends** 
05-05 16:12:07.145: V/TokenRetryPolicy(1972): Initiating a retry 
05-05 16:12:07.145: V/TokenRetryPolicy(1972): AuthFailureError found! 
05-05 16:12:07.146: V/VolleyUser(1972): refresh token task called 
05-05 16:12:07.146: V/RefreshAPICall(1972): RefreshAPICall made with username user_password 

05-05 16:12:07.147: V/RefreshAPICall(1972): Priority set on refresh call is HIGH 
05-05 16:12:07.147: V/RefreshAPICall(1972): Tag for Call is retry 
05-05 16:12:07.265: E/Volley(1972): [137] BasicNetwork.performRequest: Unexpected response code **401 for https://url.me/api/get_friends** 
05-05 16:12:07.265: V/TokenRetryPolicy(1972): Initiating a retry 
05-05 16:12:07.265: V/TokenRetryPolicy(1972): AuthFailureError found! 
05-05 16:12:07.265: V/VolleyUser(1972): refresh token task called 
05-05 16:12:07.265: V/RefreshAPICall(1972): RefreshAPICall made with user user_password 

05-05 16:12:07.265: V/RefreshAPICall(1972): Priority set on refresh call is HIGH 
05-05 16:12:07.265: V/RefreshAPICall(1972): Tag for Call is retry 
05-05 16:12:07.265: V/TokenRetryPolicy(1972): No attempt remaining, ERROR 

05-05 16:12:08.219: I/Choreographer(1972): Skipped 324 frames! The application may be doing too much work on its main thread. 
05-05 16:12:08.230: V/RefreshAPICall(1972): Response from server on refresh is: {"status":"success","token":"d5792e18c0e1acb3ad507dbae854eb2cdc5962a2c1b610a6b77e3bc3033c7f64"} 
05-05 16:12:08.230: V/VolleyUser(1972): Token from return is: d5792e18c0e1acb3ad507dbae854eb2cdc5962a2c1b610a6b77e3bc3033c7f64 
05-05 16:12:08.231: V/TokenRetryPolicy(1972): Cancelled all retry calls 
05-05 16:12:08.257: V/SessionManager(1972): New Token In SharedPref is: d5792e18c0e1acb3ad507dbae854eb2cdc5962a2c1b610a6b77e3bc3033c7f64 
05-05 16:12:08.257: V/TokenRetryPolicy(1972): Logged new token 
+0

आप इस के लिए किसी भी ठीक मिला? या कोई कामकाज? मैं कुछ इसी तरह से अटक गया हूँ। –

+0

@ सुशांतकुनल कृपया नीचे देखें, उम्मीद है कि यह मदद करता है! – Brandon

उत्तर

8

एक उत्तर पोस्ट अब है कि मैं एक आधा सभ्य तरीका पुन: प्रयास से टोकन ताज़ा को संभालने के लिए मिल गया।

जब मैं वॉली के साथ अपना सामान्य (सबसे आम) एपीआई कॉल बनाता हूं, तो मैं विफल होने पर कॉल के संदर्भ को सहेजता हूं, और इसे मेरी पुनः प्रयास नीति में पास करता हूं।

public GeneralAPICall(int method, String url, Map<String, String> params, Context context, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) { 
    super(method, url, errorListener); 
    sessionManager = SessionManager.getmInstance(context); 
    HashMap<String, String> credentials = sessionManager.getUserDetails(); // Get the user's credentials for authentication 
    this.listener = responseListener; 
    this.params = params; 
    // Encode the user's username and token 
    String loginEncoded = new String(Base64.encode((credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) 
      + Constants.APIConstants.Characters.CHAR_COLON 
      + credentials.get(Constants.SessionManagerConstants.KEY_TOKEN)).getBytes(), Base64.NO_WRAP)); 
    this.headers.put(Constants.APIConstants.BasicAuth.AUTHORIZATION, Constants.APIConstants.BasicAuth.BASIC + loginEncoded); // Set the encoded information as the header 

    setRetryPolicy(new TokenRetryPolicy(context, this)); //passing "this" saves the reference 
} 

फिर, मेरे फिर से प्रयास करें नीति कक्षा में (जो केवल DefaultRetryPolicy, फैली हुई है जब मैं कह मुझे मैं एक नया टोकन की आवश्यकता 401 त्रुटि प्राप्त करते हैं, मैं एक refreshToken कॉल बंद गोली मार एक नया एक पाने के लिए।

public class TokenRetryPolicy extends DefaultRetryPolicy implements IRefreshTokenReturn{ 
... 

@Override 
public void retry(VolleyError error) throws VolleyError { 
    mCurrentRetryCount++; //increment our retry count 
    mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier); 
    if (error instanceof AuthFailureError && sessionManager.isLoggedIn()) { 
     mCurrentRetryCount = mMaxNumRetries + 1; // Don't retry anymore, it's pointless 
     VolleyUser.refreshTokenTask(context, this); // Get new token 
    } if (!hasAttemptRemaining()) { 
     Log.v(LOG_TAG, "No attempt remaining, ERROR"); 
     throw error; 
    } 
} 
... 

} 

एक बार जब कॉल रिटर्न हो जाता है, तो मैं अपनी पुनः प्रयास नीति वर्ग में प्रतिक्रिया को संभालता हूं। मैं उस कॉल को संशोधित करता हूं जो असफल हो जाता है, इसे नया टोकन (साझा किए गए शेयरों में टोकन संग्रहीत करने के बाद) को प्रमाणित करने के लिए, और फिर इसे फिर से आग लगाना!

@Override 
public void onTokenRefreshComplete(String token, String expiration) { 
    sessionManager.setStoredToken(token, expiration); 

    HashMap<String, String> credentials = sessionManager.getUserDetails(); //get the user's credentials for authentication 

    //encode the user's username and token 
    String loginEncoded = new String(Base64.encode((credentials.get(Constants.SessionManagerConstants.KEY_USERNAME) 
      + Constants.APIConstants.Characters.CHAR_COLON 
      + credentials.get(Constants.SessionManagerConstants.KEY_TOKEN)).getBytes(), Base64.NO_WRAP)); 
    Log.v(LOG_TAG, loginEncoded); //TODO: remove 
    callThatFailed.setHeaders(Constants.APIConstants.BasicAuth.AUTHORIZATION, Constants.APIConstants.BasicAuth.BASIC + loginEncoded); //modify "old, failed" call - set the encoded information as the header 

    VolleySingleton.getInstance(context).getRequestQueue().add(callThatFailed); 
    Log.v(LOG_TAG, "fired off new call"); 
} 

यह कार्यान्वयन मेरे लिए बहुत अच्छा काम करता है।

हालांकि, मैं नोट करना चाहिए कि इस स्थिति ज्यादा नहीं होना चाहिए क्योंकि मुझे पता चला कि मेरा टोकन से पहले किसी भी API कॉल करने समाप्त हो गया है कि मैं जांच होनी चाहिए। SharePrefs में एक समाप्ति समय (सर्वर से लौटाया गया) संग्रहीत करना संभव है, और अगर वर्तमान_टाइम - समाप्ति समय < कुछ_टाइम को देखकर संभव है, तो कुछ समय के साथ आप इसे समाप्त होने से पहले एक नया टोकन प्राप्त करना चाहते हैं, मेरे लिए 10 सेकंड।

उम्मीद है कि यह किसी को वहां मदद करता है, और यदि मैं कुछ भी गलत हूं, तो कृपया टिप्पणी करें!

+0

क्या आप अपना कोड साझा कर सकते हैं ?, मुझे इसे लागू करना है, आपका कोड ऐसा है जैसे मैं करना चाहता हूं। धन्यवाद –

+0

@MaxPinto, आप किस कोड की तलाश में हैं? उपरोक्त कोड मूलभूत जानकारी देता है कि इसे कैसे कार्यान्वित किया जाए। – Brandon

+0

अच्छी तरह से हाँ मैं समझता हूं कि आपका कोड मूलभूत बातें देता है, लेकिन विशेष रूप से आप यह फ़ाइल कैसे करते हैं: आईआरआईफ़्रेश टोकन रिटर्न इंटरफ़ेस –

0

मुझे यह पुराना पोस्ट पता है, लेकिन अन्य समाधानों के बाद मेरा समाधान पोस्ट करने से मुझे मदद नहीं मिली।

नोट - मैंने ऊपर दिए गए ब्रैंडन की विधि का प्रयास किया, यानी, डिफ़ॉल्टRetryPolicy का विस्तार किया। लेकिन यह क्षेत्र निजी हैं, इसलिए पूरे वर्ग को लागू नहीं करना चाहते थे, वहां एक बेहतर तरीका होना चाहिए था।

तो मैं कस्टमरवेस्ट क्लास विस्तार अनुरोध में कोड लिखता हूं।

लॉगिन जवाब में

स्टोर टोकन - -

@Override 
protected Response<T> parseNetworkResponse(NetworkResponse response) { 
    ... 
    //if oauth data is sent with response, store in SharedPrefs 
    ... 
} 

तो पहुँच टोकन समाप्त हो गया है -

@Override 
protected VolleyError parseNetworkError(VolleyError volleyError) { 
... 
if (volleyError instanceof NoConnectionError) { 
     //i know, there has to be a better way than checking this. 
     //will work on it later 
     if(volleyError.getMessage().equalsIgnoreCase("java.io.IOException: No authentication challenges found")) { 

      String accessToken = getNewAccessToken();//synchronous call 

      //retry 
      if(accessToken != null) { 
       //IMP: this is the statement which will retry the request manually 
       NetworkHelper.get(mContext).getRequestQueue().add(this); 
      } 
     } 
    } 
    ... 
} 

संलग्न पहुँच टोकन का अनुरोध करने के -

@Override 
public Map<String, String> getHeaders() throws AuthFailureError { 
    ... 
    String accesssToken = //get from SharedPrefs 
    headers.put("Authorization", "Bearer " +accessToken); 
    ... 
} 

के लिए जा रहे यहाँ प्रासंगिक स्निपेट होते हैं, रीफ्रेश टोकन अमान्य है अगर लॉगिन स्क्रीन -

private void showLogin(){ 
    //stop all current requests 
    //cancelAllRequests(); 

    Intent intent = new Intent(mContext, LoginActivity.class); 
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 
    mContext.startActivity(intent); 
} 

ताज़ा टोकन का उपयोग करके नई पहुंच टोकन प्राप्त करना। यह एक तुल्यकालिक विधि RequestFuture का उपयोग कर हो गया है -

private String getNewAccessToken(){ 
    ... 
    //get new access token from server and store in SharedPrefs 
    ... 
    //also return the new token so that we know if we need to retry or not 
    return newAccessToken; 
} 

HTH

+0

वॉली के माध्यम से लड़ने के कुछ ही समय बाद मैं ईमानदार रहूंगा, थोड़ा सा मैं रेट्रोफिट करने का अधिकार चला गया। यह ओकेएचटीपी के साथ जोड़ा गया वॉली से बेहतर तरीका है और आप सभी रीट्रीज और सबकुछ करने के लिए 'प्रमाणीकरणकर्ता' का उपयोग कर सकते हैं। यह ऐसा कुछ करने से अधिक सहज अनुभव है। मल्टीपार्ट फ़ाइलों को संभालने के लिए – Brandon

+0

ओकेएचटीपी निश्चित रूप से मेरे लिए बेहतर था। मैं प्रमाणीकरणकर्ताओं का उपयोग करने से सावधान था क्योंकि सर्वर पर ओएथ कोड एक कस्टम है। मैं अगली बार रेट्रोफिट कोशिश करूंगा। – Medha

संबंधित मुद्दे