REST方法 - 执行" common"方法调用之前的其他方法

时间:2017-04-03 10:23:25

标签: java android rest asynchronous retrofit

我有一个调用REST API的Android应用。 REST API使用JWT进行保护,当用户登录应用程序时,我获得了android的令牌。令牌在60分钟后到期。

在我的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工作来检查令牌并从服务器获取它。任何其他REST调用应在renew()执行完毕后运行。 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调用绝对不是一个好的模式。

1 个答案:

答案 0 :(得分: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

进行

我为此建议的解决方案(Thread和join())是脏的,我认为它会抛出一些异常,比如NetworkOnMainThread ...希望这会以任何方式帮助你:)