欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-02-24 18:54:28
概述 随着google对httpclient 摒弃,和volley的逐渐没落,okhttp开始异军突起,而retrofit则对okhttp进行了强制依赖。 retrof...

概述

随着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://www.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<response<object>> search(@query("wd")string wd);
 
 @get("s")
 @headers({urlconstants.header.rest_version_v1})
 observable<response<object>> searchchangepath(@query("wd")string wd);
 
 @get("s")
 @headers({urlconstants.header.host_dev})
 observable<response<object>> searchchangehost(@query("wd")string wd);
 
 @headers({urlconstants.header.host_private, urlconstants.header.rest_version_private})
 @get("s")
 observable<response<object>> 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.encodedquery())
    .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<string, string> hostmap;
private map<string, string> 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://www.baidu.com/rest/v2/s?wd=123
九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.platform log
信息: --> get http/1.1
九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.platform log
信息: <-- 302 found (83ms, 154-byte body)
九月 07, 2018 11:36:58 上午 okhttp3.internal.platform.platform log
信息: --> get http://www.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://www.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 http/1.1
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: <-- 302 found (154ms, 154-byte body)
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: --> get http/1.1
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: <-- 301 moved permanently (18ms, 73-byte body)
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: --> get http/1.1
hosttype:null
restversion:v1
newurl:https://www.baidu.com/rest/v1/s?wd=123
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: <-- 200 ok (157ms, unknown-length body)
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: --> get http/1.1
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: <-- 302 found (46ms, 154-byte body)
九月 07, 2018 11:36:59 上午 okhttp3.internal.platform.platform log
信息: --> get http://www.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://www.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 (本地下载

总结

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