Retrofit2利用Converter实现加解密,Mock Interceptor模拟请求数据,并在Junit中测试

 

本文根据以下三篇博客总结得出:

小张也是蛮拼的 - Retrofit 2 之自定义Converter实现加密解密

唯鹿 - Android单元测试(五):网络接口测试 

majunm Retrofit2封装之路(请求参数加密解密)(二)

写此文只为方便日后回顾。

 

 

 

首先是自定义的 JsonConverterFactory,在这个转换器工厂类里添加了一个boolean值开关,用户决定是否对传入传出的数据进行加密解密

import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;

public class JsonConverterFactory extends Converter.Factory {
    private static boolean isEncrypt = true;

    public static JsonConverterFactory create() {
        return create(new GsonBuilder().setLenient().create());
    }

    /**
     * 是否对进入的参数和返回的结果加密解密
     * @param encrypt
     * @return
     */
    public static JsonConverterFactory create(boolean encrypt) {
        isEncrypt = encrypt;
        return create(new GsonBuilder().setLenient().create());
    }

    public static JsonConverterFactory create(Gson gson) {
        if (gson == null)
            throw new NullPointerException("gson == null");
        return new JsonConverterFactory(gson);
    }

    private final Gson gson;

    private JsonConverterFactory(Gson gson) {
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type,
                                                            Annotation[] annotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new JsonResponseBodyConverter(gson, adapter,isEncrypt); // 响应
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations,
                                                          Retrofit retrofit) {
        System.out.println("#发起请求#");
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new JsonRequestBodyConverter(gson, adapter,isEncrypt); // 请求
    }
}

--- 请求体转化器

import com.ee56.scangun.utils.crypto.MyAES;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;

import java.io.IOException;
import java.nio.charset.Charset;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import retrofit2.Converter;

/**
 * Created by zhang on 2016/5/31.
 * <p>
 * 自定义请求RequestBody
 */
public class JsonRequestBodyConverter implements Converter<Object, RequestBody> {
    private static final MediaType MEDIA_TYPE = MediaType
            .parse("application/json; charset=UTF-8");
    static final Charset UTF_8 = Charset.forName("UTF-8");

    final Gson gson;
    final TypeAdapter adapter;
    boolean isEncrypt;

    JsonRequestBodyConverter(Gson gson, TypeAdapter adapter,boolean isEncrypt) {
        this.gson = gson;
        this.adapter = adapter;
        this.isEncrypt = isEncrypt;
        System.out.println("#IRequestBodyConverter初始化#");
    }

    @Override
    public RequestBody convert(Object value) throws IOException {
        //将传入的实体类先转成json,再进行加密
        String json = gson.toJson(value);
        if(isEncrypt) {
            System.out.println("#加密前#" + json);
            try {
                json = MyAES.aesEncryptT(json);
            } catch (Exception ex) {
                System.out.println("#加密出错#" + ex.getMessage());
                json = "";
            }
            System.out.println("#加密后#" + json);
        }
        return RequestBody.create(MEDIA_TYPE, json);
    }
}

--- 回应体转换器

import com.ee56.scangun.utils.crypto.MyAES;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;

import java.io.IOException;
import okhttp3.ResponseBody;
import retrofit2.Converter;

/**
 * Created by zhang on 2016/5/31.
 * 自定义响应ResponseBody
 */
public class JsonResponseBodyConverter implements Converter<ResponseBody,String> {
    private final Gson gson;
    private final TypeAdapter adapter;
    private boolean isEncrypt;

    JsonResponseBodyConverter(Gson gson, TypeAdapter adapter,boolean isEncrypt) {
        this.gson = gson;
        this.adapter = adapter;
        this.isEncrypt = isEncrypt;
    }

    @Override
    public String convert(ResponseBody value) throws IOException {
        //得到请求返回体的内容
        String string = value.string();
        if(isEncrypt) {
            System.out.println("#解密前@#" + string);
            try {
                string = MyAES.aesDecrypt(string);
            } catch (Exception e) {
                System.out.println("#解密出错@#" + e.getMessage());
                string = "";
            }
            System.out.println("#解密后@#" + string);
        }
        return string;
    }

}

 

 

接下来对请求类进行封装,

import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

import com.google.gson.Gson;

import java.io.IOException;

public class HttpManager {
    private static final Gson GSON = new Gson();
	private OkHttpClient mOkHttpClient;
	private Retrofit mRetrofit;

	private HttpManager() {
		mOkHttpClient = getOkHttpClient();
		mRetrofit = new Retrofit.Builder()
                .baseUrl(GithubApi.BASE_URL)
				.client(mOkHttpClient)
                //添加转换器时,create()不添加参数表示加密
				.addConverterFactory(JsonConverterFactory.create())
                .build();
	}

	private static final class HttpManagerHolder {
		private static final HttpManager INSTANCE = new HttpManager();
	}

	public static HttpManager getInstance() {
		return HttpManagerHolder.INSTANCE;
	}

	public static <T> T doGet(String subUrl,Class<T> type){
		return getInstance().get(subUrl,type);
	}

	public static void doPost(Object src, String subURL,final ResultCallback<?> mCallback) {
		getInstance().post(src,subURL,mCallback);
	}

    /**
     * url是请求的全路径
     * @param url
     */
	public <T> T get(String url,Class<T> type) {
	    //先把请求url拆解成基础路径和子路径
	    String baseUrl = url.substring(0,url.lastIndexOf("/")+1);
	    String subUrl = url.substring(url.lastIndexOf("/")+1);
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(mOkHttpClient)
                .build();
		GithubApi api = retrofit.create(GithubApi.class);
		Call<ResponseBody> doGet = api.doGet(subUrl);
        System.out.println("#成功回调#");
        String res = "";
        try {
            Response<ResponseBody> response = doGet.execute();
            if(response.isSuccessful()){
                res = response.body().string();
            }
        } catch (IOException e) {
            e.printStackTrace();
            res = "";
        }
        if(!isEmpty(res)){
            if(type != null && type != String.class){
                T o = GSON.fromJson(res, type);
                return o;
            }
        }
        return (T) res;
    }


	private boolean isEmpty(String str){
		return str == null || str.equals("") || str.equals("null");
	}

    /**
     * 在ResultCallback中可以指定返回的实体类型
     * @param src
     * @param subURL
     * @param mCallback
     * @param <T>
     */
	public <T> void post(Object src,String subURL, final ResultCallback<T> mCallback) {
		GithubApi api = mRetrofit.create(GithubApi.class);
		Call<String> doPost = api.doPost(src,subURL);
		doPost.enqueue(new Callback<String>() {
			@Override
			public void onFailure(Call<String> request, Throwable error) {
				System.out.println("#失败回调#" + error.getMessage());
				if (mCallback != null) {
					mCallback.onError(null, error);
				}
			}

			@Override
			public void onResponse(Call<String> call, Response<String> resp) {
				System.out.println("#成功回调#");
				if (mCallback != null) {
					if (resp.isSuccessful()) {
						String finalStr = resp.body();
						if (mCallback.mType != null) {
							if (mCallback.mType == String.class) {
								if (isEmpty(finalStr)) {
									Exception e = new Exception(
											"AesEncryptionUtil.decrypt(string) return null!");
									exeFailedCallback(call, e, mCallback);
								} else {
									exeSuccessCallback(finalStr, mCallback);
								}
							} else {
								Object o = GSON.fromJson(finalStr,
										mCallback.mType);
								System.out.println("#类型是#" + mCallback.mType);
								if (o == null) {
									Exception e = new Exception(
											"mGson.fromJson(finalStr,callback.mType) return null!");
									exeFailedCallback(call, e, mCallback);
								} else {
									exeSuccessCallback(o, mCallback);
								}
							}
						} else {
							Throwable e = new Throwable("mCallback.mType 为空!!!");
							exeFailedCallback(call, e, mCallback);
						}
					} else {
						Throwable e = new Throwable(resp.toString());
						exeFailedCallback(call, e, mCallback);
					}
				}
			}
		});
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	public void exeFailedCallback(final Call call, final Throwable e,
								  final ResultCallback<?> callback) {
		if (callback != null) {
			callback.onError(call, e);
		}
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	public void exeSuccessCallback(Object o, final ResultCallback callback) {
		if (callback != null) {
			callback.onResponse(o);
		}
	}

	public static OkHttpClient getOkHttpClient() {
		// 日志显示级别
		HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.BODY;
		// 新建log拦截器
		HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(
				new HttpLoggingInterceptor.Logger() {
					@Override
					public void log(String message) {
						System.out.println(message);
					}
				});
		loggingInterceptor.setLevel(level);
		// 定制OkHttp
		OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
		// OkHttp进行添加拦截器loggingInterceptor
		httpClientBuilder.addInterceptor(loggingInterceptor);
		return httpClientBuilder.build();
	}
}

 

 

其中在初始化 OkHttpClient 对象的时候,可以添加Interceptor来模拟返回的数据,下面是我修改后的Mock Interceptor,

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class MockInterceptor implements Interceptor {

    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {

        String responseString = createResponseBody(chain);

        Response response = new Response.Builder()
                .code(200)
                .message(responseString)
                .request(chain.request())
                .protocol(Protocol.HTTP_1_0)
                .body(ResponseBody.create(MediaType.parse("application/json"), responseString.getBytes()))
                .addHeader("content-type", "application/json")
                .build();
        return response;
    }

    /**
     * 读文件获取json字符串,生成ResponseBody
     * 将json文件放在 test/resource/json/ 目录下,如果没有,新建之
     * @param chain
     * @return
     */
    private String createResponseBody(Chain chain) {

        String responseString = null;

        HttpUrl uri = chain.request().url();
        String path = uri.url().getPath();

        System.out.println("url :"+path);

        if (path.matches("^(/users/)+[^/]*+(/repos)$")) {//匹配/users/{username}/repos
            responseString = getResponseString("users_repos.json");
        } else if (path.matches("^(/users/)+[^/]+(/following)$")) {//匹配/users/{username}/following
            responseString = getResponseString("users_following.json");
        } else if (path.matches("^(/users/)+[^/]*+$")) {//匹配/users/{username}
            responseString = getResponseString("users.json");
        }else if(path.matches("^(/WarehouseServices/WH_UserLoginByHandheld)")){
            responseString = getResponseString("login.json");
        }
        return responseString;
    }



    //将文件中的json数据读取出来,json文本放在 test/resource/json/目录下
    private String getResponseString(String fileName){
        FileInputStream fis;
        File currentDir = new File("");
        File currentFile = new File(currentDir.getAbsolutePath()+"/src/test/resource/json/"+fileName);
        try {
            fis = new FileInputStream(currentFile);
        }catch (FileNotFoundException ffe){
            fis = null;
            System.out.println("file not found exception :"+ffe.getMessage());
        }
        StringBuilder stringBuffer = new StringBuilder();
        if(fis!=null){
            BufferedInputStream bif = new BufferedInputStream(fis);
            byte[] buffer = new byte[1024];
            try {
                while ((bif.read(buffer,0,buffer.length))!=-1){
                    stringBuffer.append(new String(buffer));
                }
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                try {
                    bif.close();
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stringBuffer.toString();
    }

}

 

对HttpManager进一步封装的调用类

public class HttpRequestFactory {
 
	public static <T> void post(Object src, String subURL,final ResultCallback<T> mCallback) {
		HttpManager.doPost(src,subURL, mCallback);
	}

	public static <T> T get(String url,Class<T> type) {
		return HttpManager.doGet(url,type);
	}

    public static String get(String url) {
        return HttpManager.doGet(url,String.class);
    }
}
import com.google.gson.internal.$Gson$Types;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import retrofit2.Call;

public abstract class ResultCallback<T> {
	public Type mType;

	public ResultCallback() {
		mType = getSuperclassTypeParameter(getClass());
	}

	private static Type getSuperclassTypeParameter(Class<?> subclass) {
		Type superclass = subclass.getGenericSuperclass();
		if (superclass instanceof Class) {
			throw new RuntimeException("Missing type parameter.");
		}
		ParameterizedType parameterized = (ParameterizedType) superclass;
		return $Gson$Types
				.canonicalize(parameterized.getActualTypeArguments()[0]);
	}

	public abstract void onError(Call<T> request, Throwable e);

	public abstract void onResponse(T response);
}

 

 

retrofit Api 接口类

public interface GithubApi {

    String BASE_URL = "http://xxx.xxx.com/xxxx/";

    @GET("{path}")
    Call<ResponseBody> doGet(@Path("path") String path);

    /**
     * 传入实体类
     * @param param
     * @return
     */
    @POST("{path}")
    Call<String> doPost(@Body Object param,@Path("path") String path);

}

 

 

最后在Junit测试用例中调用

@RunWith(BlockJUnit4ClassRunner.class)
public class ExampleUnitTest {

    private CountDownLatch downLatch;

    @Rule
    public RxJavaRule rxJavaRule = new RxJavaRule();

    @Before
    public void setUp() {
        downLatch = new CountDownLatch(1);
    }



    @Test
    public void testGet () {
        String url = "xxxxxxxxxx";
        UpdateBean res = HttpRequestFactory.get(url,UpdateBean.class);
        System.out.println("#--x--#" + res.getReleaseDate());
        System.out.println("#--x--#" + res.getDownloadUrl());
    }


    @Test
    public void testPost() {
        String username = "xxxxxxxxxxx";
        String password = "123456";
        //构造用户登录json对象
        UserLoginPostBean postBean = new UserLoginPostBean();
        postBean.setLoginName(username);
        postBean.setPassword(password);

        HttpRequestFactory.post(postBean,"subURL子路径",
                new ResultCallback<String>() {
            @Override
            public void onError(Call<String> request, Throwable e) {
                synchronized (HttpRequestFactory.class) {
                    HttpRequestFactory.class.notify();
                }
            }

            @Override
            public void onResponse(String response) {
                synchronized (HttpRequestFactory.class) {
                    HttpRequestFactory.class.notify();
                }
            }
        });
        try {
            synchronized (HttpRequestFactory.class) {
                HttpRequestFactory.class.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("方法结束,我最后执行");

    }





}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值