2017-04-03 155 views
1

我有一个调用REST API的Android应用程序。 REST API使用JWT进行保护,当用户登录到应用程序时,我得到了android的令牌。该令牌在60分钟内过期。REST方法 - 在执行其他方法之前执行“常用”方法调用

在我的android应用程序中,我有不同的类,它们总共包含50-60个REST调用。 Android应用程序使用Retrofit连接到这些REST方法。我有一些方法在执行另一个方法后需要工作,这些方法在第一个方法的onResponse方法中。

我有一个特殊的课程,名为Token,其中setget是来自变量的JWT标记。每个REST调用都会从这个类中获取令牌。

无论如何,由于REST使用JWT来保护,因此我必须在50分钟后重新创建令牌。我首先要检查Token类中的token变量是否即将到期,它有一个特殊的方法whenWillExpire()来告诉我什么时候令牌将过期。如果是,则再次调用REST API并获取新的令牌。

这种情况是,因为我不能告诉哪个REST调用我必须重新创建令牌,所以我必须在any REST调用之前执行此令牌过期检查,并获得新令牌(如果过期)选择的REST方法被调用。举一个例子,假设我有一个名为renew()的方法,它检查令牌并通过执行REST工作从服务器获取它。执行完renew()之后,应该运行其他REST调用。 renew()应该首先在每个REST呼叫请求上运行。这是这种方法的一个例子。

private void renew(String token) { 

if(token expired){ 
    Gson gson = new GsonBuilder() 
      .setDateFormat("yyyy-MM-dd'T'HH:mm:ss") 
      .create(); 

Retrofit retrofit = new Retrofit.Builder() 
     .baseUrl(RestCommon.URL) 
     .addConverterFactory(GsonConverterFactory.create(gson)) 
     .build(); 

GetNewToken endPoint = retrofit.create(GetNewToken.class); 
Call<ResponseBody> call = endPoint.getNewToken(); 
call.enqueue(new Callback<ResponseBody>() { 
    @Override 
    public void onResponse(Response<ResponseBody> response, Retrofit retrofit) { 
     // Good. Now we have the token. Call other methods here. 
     } 

    @Override 
    public void onFailure(Throwable t) { 
     t.printStackTrace(); 
    } 
}); 
} 
    else 
    { 
     //Not expired. Call other methods. 
    } 
    } 

所以,我怎样才能确保renew()运行在每一个REST请求,正在执行的请求的方法之前?什么是最好的设计呢?我喜欢50-60个REST方法,并用不同的名字复制上面的代码,并在其中添加其他REST调用绝对不是一个好的模式。

+0

这是一个聪明的设计,我通常发生在一个'interface'所有的API调用,我有一个创建'改造改造的1个实例类...'(单),并在所有的呼叫使用它,而且,通常我创建一个类(包装),其对每个API端点的方法在'interface'并在这方法我称之为使用'retrofit'实例调用API,我传递'callback'作为方法匿名内部类,它可能看起来复杂,我可以发布,如果你的答案想。重点是在包装方法调用,你可以做所有你想要做的检查,包括令牌过期,更新呼叫等等 – Yazan

+0

谢谢,请发表回答。但我也有REST调用,它们必须一个接一个地运行。有时候这个链条长6-7个方法。这也支持? –

+0

是的,你会在'onResponse()''onFail()'中传递匿名内部类作为'CallBack',你可以做任何你想做的事。我将在几分钟内显示ua示例 – Yazan

回答

0

定义的所有API端点:

public interface MyApi { 
    @GET("item/{id}") 
    Call<Item> getItemById(@Path("id") String id, @Header("Authorization") String auth) 

    @DELETE("item/{id}") 
    Call<String> deleteItemById(@Path("id") String id, @Header("Authorization") String auth) 

    @POST("auth") 
    Call<String> getNewToken(@Field("token") String currentToken);// or other params needed by getNewToken 

    //...etc 
} 

包装类:

public class MyApiManager{ //?? chose other name if u want :) 

    private MyApi myApi = null; 
    private static MyApiManager instance = null; 

    public MyApiManager getInstance(){ 
     if(instance == null){ 
      instance = new MyApiManager(); 
     } 
     return instance; 
    } 

    private MyApiManager(){ 
     Retrofit retrofit = new Retrofit.Builder() 
     .baseUrl("https://api.host.name") 
     //other setting before build() ... 
     .build(); 

     myApi = retrofit.create(MyApi.class); 
    } 

    //-------- General methods, like check token expired... 

    private boolean checkAndGetNewToken(){ 
     if(token expired){ 
      Thread t = new Thread(new Runnable() { 
       @Override 
       public void run() { 
        //note here using myApi directly to avoid recursive calls for checkAndGetNewToken() 
        Response<String> response = myApi.getNewToken(token).execute(); 
        Token.set(response);//update your token 
       }//run() 
      }); 
      t.start(); 
      t.join(); 
     }//token exp 
     return true; 
    }//checkToken 

    //------ wrapper methods for each API endpoint 

    public Call<Item> getItemById(String id) throws TokenExpiredException{ 
     checkTokenExpiration(); 
     return myApi.getItemById(id, "Authorization:Bearer "+Token.get()); 
    } 

    public Call<Item> deleteItemById(String id) throws TokenExpiredException{ 
     checkTokenExpiration(); 
     return myApi.deleteItemById(id, "Authorization:Bearer "+Token.get()); 
    } 

    public Call<Item> getNewToken(String currentToken) throws TokenExpiredException{ 
     checkTokenExpiration(); 
     return myApi.getNewToken(currentToken); 
    } 
} 

用例:

public class SomeClass { 
    public void someMethod(){ 
     MyApiManager.getInstance().getItem("102").enqueue(new Callback<Item>() { 
      @Override 
      public void onResponse(Response<Item> response, Retrofit retrofit) { 
       callApiStageB(response.getBody().getId()); //? 
      } 

      @Override 
      public void onFailure(Throwable t) { 
       t.printStackTrace(); 
       //show a toast ... 
      } 
     }); 
    } 

    public void callApiStageB(String id){ 
     MyApiManager.getInstance().deleteItemById(id).enqueue(new Callback<String>() { 
      @Override 
      public void onResponse(Response<String> response, Retrofit retrofit) { 
       //....more nested api-calls, or do anything else 
      } 

      @Override 
      public void onFailure(Throwable t) { 
       t.printStackTrace(); 
       //show a toast ... 
      } 
     }); 
    } 
} 

,你可以看到,每个呼叫使用apiManager会首先检查/请求新的令牌,问题是当需要新的令牌时,处理这个部分,但是如何返回并执行原来的请求(ex getItem())在这一步之后,因为它应该通过enqueue()CallBack

我建议sol这个(线程和连接())是肮脏的,我认为它会抛出一些例外,比如NetworkOnMainThread ...希望这有助于你以任何方式:)

相关问题