如何动态改变Retrofit的base url和rest版本详解

网友投稿 648 2023-01-22


如何动态改变Retrofit的base url和rest版本详解

概述

随着Google对HttpClient 摒弃,和Volley的逐渐没落,OkHttp开始异军突起,而Retrofit则对okHttp进行了强制依赖。

Retrofit是由Square公司出品的针对于android和java的类型安全的Http客户端,

如果看源码会发现其实质上就是对okHttp的封装,使用面向接口的方式进行网络请求,利用动态生成的代理类封装了网络接口请求的底层,

其将请求返回javaBean,对网络认证 REST API进行了很好对支持此,使用Retrofit将会极大的提高我们应用的网络体验。

REST

既然是RESTful架构,那么我们就来看一下什么是REST吧。

REST(REpresentational State Transfer)是一组架构约束条件和原则。

RESTful架构都满足以下规则:

(1)每一个URI代表一种资源;

(2)客户端和服务器之间,传递这种资源的某种表现层;

(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”。

下面话不多说了,来开始本文的正文吧

1. 需求与前提

base url

默认base url: https://cloud.devwiki.net

测试版 url : https://dev.devwiki.net

私有云版本url: https://private.devwiki.net

rest 版本

/rest/v1/

/rest/v2/

/rest/v3/

需求点

大部分接口使用 cloud host, 部分接口使用 private host

大部分接口使用 rest/v3 版本, 部分接口使用 v2, v1版本.

每个host 都有可能存在 rest v1, v2, v3的接口

2. 实现思路

okhttp 可以添加拦截器, 可在发起访问前进行拦截, 通常我们会在 拦截器中统一添加 header, 比如:

class HeaderInterceptor implements Interceptor {

private static final String ENCODING_GZIP = "gzip";

private static final String CONTENT_TYPE_jsON = "application/json;charset=UTF-8";

private static final String HEADER_CONTENT_TYPE = "Content-Type";

private static final String HEADER_ACCEPT_TYPE = "application/json";

private static final String HEADER_CONTENT_ENCODING = "Content-Encoding";

private final static String CHARSET = "UTF-8";

@Override

public Response intercept(Chain chain) throws IOException {

Request originRequest = chain.request();

Request.Builder newBuilder = originRequest.newBuilder();

newBuilder.addHeader("Accept", HEADER_ACCEPT_TYPE);

newBuilder.addHeader("Accept-Charset", CHARSET);

newBuilder.addHeader("Accept-Encoding", ENCODING_GZIP);

newBuilder.addHeader("Accept-Language", Locale.getDefault().toString().replace("_", "-"));

newBuilder.addHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);

return chain.proceed(newBuilder.build());

}

}

同理我们也可以在所有请求中添加统一的uuid 或者 key 进行防劫持或者认证. 比如:

Request originRequest = chain.request();

if (paramsMap != null) {

HttpUrl originUrl = originRequest.url();

HttpUrl.Builder newBuilder = originUrl.newBuilder();

for (String key : paramsMap.keySet()) {

newBuilder.addEncodedQueryParameter(key, paramsMap.get(key));

}

HttpUrl newUrl = newBuilder.build();

Request newRequest = originRequest.newBuilder().url(newUrl).build();

return chain.proceed(newRequest);

}

return chain.proceed(originRequest);

那么, 同样我们可以再拦截器中进行host 和 path的替换, 那么怎么替换呢?

3. 实现过程

3.1 定义host 类型和 rest 版本

host类型:

interface HostName {

String CLOUD = "CLOUD";

String PRIVATE = "PRIVATE";

String DEV = "DEV";

}

interface HostValue {

String CLOUD = "https://baidu.com";

String PRIVATE = "https://private.bidu.com";

String DEV = "https://dev.baidu.com";

}

rest 版本:

interface RestVersionCode {

String EMPTY = "EMPTY";

String V1 = "V1";

String V2 = "V2";

String PRIVATE = "PRIVATE";

}

/**

* path 前缀值

*/

interface RestVersionValue {

String EMPTY = "";

String V1 = "rest/v1";

String V2 = "rest/v2";

String PRIVATE = "rest/private";

}

设置一个默认的 host 和 rest 版本, 然后在需要更改host和rest 版本的请求接口处添header, 根据header设置来变更.

interface BaiduApiService {

@GET("s")

Observable> search(@Query("wd")String wd);

@GET("s")

@Headers({UrlConstants.Header.REST_VERSION_V1})

Observable> searchChangePath(@Query("wd")String wd);

@GET("s")

@Headers({UrlConstants.Header.HOST_DEV})

Observable> searchChangeHost(@Query("wd")String wd);

@Headers({UrlConstants.Header.HOST_PRIVATE, UrlConstants.Header.REST_VERSION_PRIVATE})

@GET("s")

Observable> searchChangeHostPath(@Query("wd")String wd);

}

header 的可选值:

interface Header {

String SPLIT_COLON = ":";

String HOST = "HostName";

String HOST_CLOUD = HOST + SPLIT_COLON + HostName.CLOUD;

String HOST_PRIVATE = HOST + SPLIT_COLON + HostName.PRIVATE;

String HOST_DEV = HOST + SPLIT_COLON + HostName.DEV;

String REST_VERSION = "RestVersion";

String REST_VERSION_V1 = REST_VERSION + SPLIT_COLON + RestVersionCode.V1;

String REST_VERSION_V2 = REST_VERSION + SPLIT_COLON + RestVersionCode.V2;

String REST_VERSION_PRIVATE = REST_VERSION + SPLIT_COLON + RestVersionCode.PRIVATE;

String REST_VERSION_EMPTY = REST_VERSION + SPLIT_COLON + RestVersionCode.EMPTY;

}

然后是解析:

class RequestInterceptor implements Interceptor {

@Override

public Response intercept(Chain chain) throws IOException {

Request originRequest = chain.request();

HttpUrl originUrl = originRequest.url();

HttpUrl.Builder newBuilder;

String hostType = originRequest.header(UrlConstants.Header.HOST);

System.out.println("hostType:" + hostType);

if (hostType != null && hostType.length() > 0) {

String hostValue = UrlManager.getInstance().getHost(hostType);

HttpUrl temp = HttpUrl.parse(hostValue);

if (temp == null) {

throw new IllegalArgumentException(hostType + "对应的host地址不合法:" + hostValue);

}

newBuilder = temp.newBuilder();

} else {

newBuilder = new HttpUrl.Builder()

.scheme(originUrl.scheme())

.host(originUrl.host())

.port(originUrl.port());

}

String restVersion = originRequest.header(UrlConstants.Header.REST_VERSION);

System.out.println("restVersion:" + restVersion);

if (restVersion == null) {

restVersion = UrlConstants.RestVersionCode.V2;

}

String restValue = UrlManager.getInstance().getRest(restVersion);

if (restValue.contains("/")) {

String[] paths = restValue.split("/");

for (String path : paths) {

newBuilder.addEncodedPathSegment(path);

}

} else {

newBuilder.addEncodedPathSegment(restValue);

}

for (int i = 0; i < originUrl.pathSegments().size(); i++) {

newBuilder.addEncodedPathSegment(originUrl.encodedPathSegments().get(i));

}

newBuilder.encodedPassword(originUrl.encodedPassword())

.encodedUsername(originUrl.encodedUsername())

.encodedQuery(originUrl.ehttp://ncodedQuery())

.encodedFragment(originUrl.encodedFragment());

HttpUrl newUrl = newBuilder.build();

System.out.println("newUrl:" + newUrl.toString());

Request newRequest = originRequest.newBuilder().url(newUrl).build();

return chain.proceed(newRequest);

}

}

为了能动态设置host, 我们需要一个map来存储host 类型和值.

private Map hostMap;

private Map restMap;

private UrlManager() {

hostMap = new HashMap<>(16);

for (UrlConstants.Host host : UrlConstants.Host.values()) {

hostMap.put(host.getName(), host.getValue());

}

restMap = new HashMap<>();

for (UrlConstants.Rest rest : UrlConstants.Rest.values()) {

restMap.put(rest.getVersion(), rest.getValue());

}

}

//更新host 的值

public void setHost(String name, String value) {

if (hostMap.containsKey(name)) {

HttpUrl httpUrl = HttpUrl.parse(value);

if (httpUrl == null) {

throw new IllegalArgumentException("要存入的Host " + name + "对应的value:"

+ value + "不合法!");

}

hostMap.put(name, value);

} else {

throw new NoSuchElementException("没有找到已经定义的Host名称:" + name + ",请先在" +

"net.devwiki.manager.UrlConstants.Host中定义!");

}

}

//根据host 获取值

public String getHost(String name) {

if (!hostMap.containsKey(name)) {

throw new NoSuchElementException("没有找到已经定义的Host名称:" + name + ",请先在" +

"net.devwiki.manager.UrlConstants.Host中定义!");

}

return hostMap.get(name);

}

这样就可以动态替换host 和 rest版本了.

4.测试运行

测试代码:

private static void testRequest() {

BaiduRest rest = new BaiduRest();

testDefault(rest);

testChangeHost(rest);

testChangePath(rest);

testChangeHostPath(rest);

}

测试运行结果:

ostType:null

restVersion:null

newUrl:https://baidu.com/rest/v2/s?wd=123

九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.Platform log

信息: --> GET https://baidu.com/rest/v2/s?wd=123 http/1.1

九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.Platform log

信息: <-- 302 Found https://baidu.com/rest/v2/s?wd=123 (83ms, 154-byte body)

九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.Platform log

信息: --> GET http://baidu.com/s?wd=123&tn=SE_PSStatistics_p1d9m0nf http/1.1

九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.Platform log

信息: <-- 200 OK http://baidu.com/s?wd=123&tn=SE_PSStatistics_p1d9m0nf (46ms, unknown-length body)

hostType:DEV

restVersion:null

newUrl:https://dev.baidu.com/rest/v2/s?wd=123

九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.Platform log

信息: --> GET https://dev.baidu.com/rest/v2/s?wd=123 http/1.1

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: <-- 302 Found https://dev.baidu.com/rest/v2/s?wd=123 (154ms, 154-byte body)

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: --> GET http://developer.baidu.com/error.html http/1.1

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: <-- 301 Moved Permanently http://developer.baidu.com/error.html (18ms, 73-byte body)

九月 07, 2018 xDRCkWbOAz11:36:59 上午 okhttp3.internal.platform.Platform log

信息: --> GET https://developer.baidu.com/error.html http/1.1

hostType:null

restVersion:V1

newUrl:https://baidu.com/rest/v1/s?wd=123

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: <-- 200 OK https://developer.baidu.com/error.html (157ms, unknown-length body)

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: --> GET https://baidu.com/rest/v1/s?wd=123 http/1.1

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: <-- 302 Found https://baidu.com/rest/v1/s?wd=123 (46ms, 154-byte body)

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: --> GET http://baidu.com/s?wd=123&tn=SE_PSStatistics_p1d9m0nf http/1.1

九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.Platform log

信息: <-- 200 OK http://baidu.com/s?wd=123&tn=SE_PSStatistics_p1d9m0nf (54ms, unknown-length body)

hostType:PRIVATE

restVersion:PRIVATE

newUrl:https://private.bidu.com/rest/private/s?wd=123

结果按照设置进行了host 和 rest 的变更.

5. 项目代码

项目代码地址: Dev-Wiki/OkHttpDemo (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:详解AOP与Filter拦截请求打印日志实用例子
下一篇:Java中比较运算符compareTo()、equals()与==的区别及应用总结
相关文章

 发表评论

暂时没有评论,来抢沙发吧~