详解Retrofit2.0 公共参数(固定参数)

本文主要介绍了Retrofit2.0 公共参数(固定参数),分享给大家,具体如下:

请先阅读:
Retrofit 动态参数(非固定参数、非必须参数)(Get、Post请求)

在实际项目中,对于有需要统一进行公共参数添加的网络请求,可以使用下面的代码来实现:

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint(ctx).setRequestInterceptor(new RequestInterceptor() {
     @Override
     public void intercept(RequestFacade request) {
      request.addQueryParam("publicParams", "1");
     }
    }).setConverter(new BaseConverter())
    .build();

在RestAdapter的实例化对象的时候,为其指定一个RequestInterceptor接口的实现类即可,在该类中,可以对请求体的相关参数进行设置,如addHeader、addQueryParam等。

不过遗憾的是Retrofit2.0已经没有了该类,该怎么做呢?通过Interceptor实现。

Interceptor是拦截器, 在发送之前, 添加一些参数, 或者获取一些信息。

/**
 * 封装公共参数(Key和密码)
 * <p>
 */
public class CommonInterceptor implements Interceptor {
 private final String mApiKey;
 private final String mApiSecret;

 public CommonInterceptor(String apiKey, String apiSecret) {
  mApiKey = apiKey;
  mApiSecret = apiSecret;
 }

 @Override public Response intercept(Interceptor.Chain chain) throws IOException {
  String marvelHash = ApiUtils.generateMarvelHash(mApiKey, mApiSecret);
  Request oldRequest = chain.request();

  // 添加新的参数
  HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
    .newBuilder()
    .scheme(oldRequest.url().scheme())
    .host(oldRequest.url().host())
    .addQueryParameter(MarvelService.PARAM_API_KEY, mApiKey)
    .addQueryParameter(MarvelService.PARAM_TIMESTAMP, ApiUtils.getUnixTimeStamp())
    .addQueryParameter(MarvelService.PARAM_HASH, marvelHash);

  // 新的请求
  Request newRequest = oldRequest.newBuilder()
    .method(oldRequest.method(), oldRequest.body())
    .url(authorizedUrlBuilder.build())
    .build();

  return chain.proceed(newRequest);
 }
}

Okhttp3使用了装饰者模式, 使用Builder添加Interceptor。

CommonInterceptor commonInterceptor = new CommonInterceptor(
    "key", "Secret");

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(commonInterceptor)
    .build();

// 适配器
Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("url")
  .addConverterFactory(GsonConverterFactory.create()
  .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  .client(client)
  .build();

有时候你找到了一条线,就能顺着线找到更多。

背景

在 Android Http API 请求开发中经常遇到这样的需求:每一次请求带上一个或者多个固定不变的参数,例如:

  1. 设备唯一标识:device_id = 7a4391e28f309c21
  2. 业务唯一标识:uid = 2231001
  3. 平台类型:platform = android
  4. 客户端版本号:version_code = 6

这些参数是每一次发生请求都需要的,我们姑且称他们为公共参数(或者基础参数)。公共参数一般以 header line、url query 或者 post body(较少) 这些形式插入请求。

实现

如果使用 OkHttp 作为 http request client, 这件事情就变得简单多了。OkHttp 提供了强大的拦截器组件 (Interceptor):

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.

也就是说,OkHttp 的拦截器功能之一就是对将要发出的请求进行拦截、改造然后再发出。这正是我们想要的。BasicParamsInterceptor 实现了 okhttp3.Interceptor 接口。

实现 public Response intercept(Chain chain) throws IOException 方法。使用 Builder 模式,暴露以下接口:

addParam(String key, String value)

post 请求,且 body type 为 x-www-form-urlencoded 时,键值对公共参数插入到 body 参数中,其他情况插入到 url query 参数中。

addParamsMap(Map paramsMap)

同上,不过这里用键值对 Map 作为参数批量插入。

addHeaderParam(String key, String value)

在 header 中插入键值对参数。

addHeaderParamsMap(Map headerParamsMap)

在 header 中插入键值对 Map 集合,批量插入。

addHeaderLine(String headerLine)

在 header 中插入 headerLine 字符串,字符串需要符合 -1 != headerLine.indexOf(“:”) 的规则,即可以解析成键值对。

addHeaderLinesList(List headerLinesList)

同上,headerLineList: List 为参数,批量插入 headerLine。

addQueryParam(String key, String value)

插入键值对参数到 url query 中。

addQueryParamsMap(Map queryParamsMap)

插入键值对参数 map 到 url query 中,批量插入。

示例

使用 Buider 模式创建 Interceptor 对象,然后调用 OkHttp 的 addInterceptor(Interceptor i) 方法将 interceptor 对象添加至 client 中:

BasicParamsInterceptor basicParamsInterceptor =
  new OkPublicParamsInterceptor.Builder()
    .addHeaderParam("device_id", DeviceUtils.getDeviceId())
    .addParam("uid", UserModel.getInstance().getUid())
    .addQueryParam("api_version", "1.1")
    .build();
OkHttpClient client = new OkHttpClient.Builder()
  .addInterceptor(basicParamsInterceptor)
  .build();

TODO

  1. 自动时间戳公共参数的支持
  2. 动态参数的支持(例如登录后插入服务器返回的 uid)

源码

源码与引用:https://github.com/jkyeo/okhttp-basicparamsinterceptor

basicparamsinterceptor应用

配置基本提交参数

我们可以建一个拦截器,这里我举例加些简单的系统参数,如下:

 class HttpBaseParamsLoggingInterceptor implements Interceptor{

  @Override
  public Response intercept(Chain chain) throws IOException {
   Request request = chain.request();
   Request.Builder requestBuilder = request.newBuilder();
   RequestBody formBody = new FormBody.Builder()
   .add("userId", "10000")
   .add("sessionToken", "E34343RDFDRGRT43RFERGFRE")
   .add("q_version", "1.1")
   .add("device_id", "android-344365")
   .add("device_os", "android")
   .add("device_osversion","6.0")
   .add("req_timestamp", System.currentTimeMillis() + "")
   .add("app_name","forums")
   .add("sign", "md5")
   .build();
   String postBodyString = Utils.bodyToString(request.body());
   postBodyString += ((postBodyString.length() > 0) ? "&" : "") + Utils.bodyToString(formBody);
   request = requestBuilder
     .post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"),
       postBodyString))
     .build();
   return chain.proceed(request);
  }
 }

上面Utils类是使用的okio.Buffer里面的工具类。通过RequestBody构建要上传的一些基本公共的参数,然后通过”&”符号在http 的body里面其他要提交参数拼接。然后再通过requestBuilder重新创建request对象,然后再通过chain.proceed(request)返回Response 。

接下来在创建OkHttpClient对象的时候修改为如下代码:

mOkHttpClient = new OkHttpClient.Builder()
  .addInterceptor(interceptor)
  .addInterceptor(new HttpBaseParamsLoggingInterceptor())
  .build();

这样就添加好了一些基本的公共参数。

下面我们借助BasicParamsInterceptor实现,代码如下:

public class BasicParamsInterceptor implements Interceptor {

 Map<String, String> queryParamsMap = new HashMap<>();
 Map<String, String> paramsMap = new HashMap<>();
 Map<String, String> headerParamsMap = new HashMap<>();
 List<String> headerLinesList = new ArrayList<>();

 private BasicParamsInterceptor() {

 }

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

  Request request = chain.request();
  Request.Builder requestBuilder = request.newBuilder();

  // process header params inject
  Headers.Builder headerBuilder = request.headers().newBuilder();
  if (headerParamsMap.size() > 0) {
   Iterator iterator = headerParamsMap.entrySet().iterator();
   while (iterator.hasNext()) {
    Map.Entry entry = (Map.Entry) iterator.next();
    headerBuilder.add((String) entry.getKey(), (String) entry.getValue());
   }
  }

  if (headerLinesList.size() > 0) {
   for (String line: headerLinesList) {
    headerBuilder.add(line);
   }
  }

  requestBuilder.headers(headerBuilder.build());
  // process header params end

  // process queryParams inject whatever it's GET or POST
  if (queryParamsMap.size() > 0) {
   injectParamsIntoUrl(request, requestBuilder, queryParamsMap);
  }
  // process header params end

  // process post body inject
  if (request.method().equals("POST") && request.body().contentType().subtype().equals("x-www-form-urlencoded")) {
   FormBody.Builder formBodyBuilder = new FormBody.Builder();
   if (paramsMap.size() > 0) {
    Iterator iterator = paramsMap.entrySet().iterator();
    while (iterator.hasNext()) {
     Map.Entry entry = (Map.Entry) iterator.next();
     formBodyBuilder.add((String) entry.getKey(), (String) entry.getValue());
    }
   }
   RequestBody formBody = formBodyBuilder.build();
   String postBodyString = bodyToString(request.body());
   postBodyString += ((postBodyString.length() > 0) ? "&" : "") + bodyToString(formBody);
   requestBuilder.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), postBodyString));
  } else { // can't inject into body, then inject into url
   injectParamsIntoUrl(request, requestBuilder, paramsMap);
  }

  request = requestBuilder.build();
  return chain.proceed(request);
 }

 // func to inject params into url
 private void injectParamsIntoUrl(Request request, Request.Builder requestBuilder, Map<String, String> paramsMap) {
  HttpUrl.Builder httpUrlBuilder = request.url().newBuilder();
  if (paramsMap.size() > 0) {
   Iterator iterator = paramsMap.entrySet().iterator();
   while (iterator.hasNext()) {
    Map.Entry entry = (Map.Entry) iterator.next();
    httpUrlBuilder.addQueryParameter((String) entry.getKey(), (String) entry.getValue());
   }
  }

  requestBuilder.url(httpUrlBuilder.build());
 }

 private static String bodyToString(final RequestBody request){
  try {
   final RequestBody copy = request;
   final Buffer buffer = new Buffer();
   if(copy != null)
    copy.writeTo(buffer);
   else
    return "";
   return buffer.readUtf8();
  }
  catch (final IOException e) {
   return "did not work";
  }
 }

 public static class Builder {

  BasicParamsInterceptor interceptor;

  public Builder() {
   interceptor = new BasicParamsInterceptor();
  }

  public Builder addParam(String key, String value) {
   interceptor.paramsMap.put(key, value);
   return this;
  }

  public Builder addParamsMap(Map<String, String> paramsMap) {
   interceptor.paramsMap.putAll(paramsMap);
   return this;
  }

  public Builder addHeaderParam(String key, String value) {
   interceptor.headerParamsMap.put(key, value);
   return this;
  }

  public Builder addHeaderParamsMap(Map<String, String> headerParamsMap) {
   interceptor.headerParamsMap.putAll(headerParamsMap);
   return this;
  }

  public Builder addHeaderLine(String headerLine) {
   int index = headerLine.indexOf(":");
   if (index == -1) {
    throw new IllegalArgumentException("Unexpected header: " + headerLine);
   }
   interceptor.headerLinesList.add(headerLine);
   return this;
  }

  public Builder addHeaderLinesList(List<String> headerLinesList) {
   for (String headerLine: headerLinesList) {
    int index = headerLine.indexOf(":");
    if (index == -1) {
     throw new IllegalArgumentException("Unexpected header: " + headerLine);
    }
    interceptor.headerLinesList.add(headerLine);
   }
   return this;
  }

  public Builder addQueryParam(String key, String value) {
   interceptor.queryParamsMap.put(key, value);
   return this;
  }

  public Builder addQueryParamsMap(Map<String, String> queryParamsMap) {
   interceptor.queryParamsMap.putAll(queryParamsMap);
   return this;
  }

  public BasicParamsInterceptor build() {
   return interceptor;
  }

 }
}

只要像上面一样配置就行了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Retrofit自定义请求参数注解的实现思路

    前言 目前我们的项目中仅使用到 GET 和 POST 两种请求方式,对于 GET 请求,请求的参数会拼接在 Url 中:对于 POST 请求来说,我们可以通过 Body 或表单来提交一些参数信息. Retrofit 中使用方式 先来看看在 Retrofit 中对于这两种请求的声明方式: GET 请求 @GET("transporter/info") Flowable<Transporter> getTransporterInfo(@Query("uid"

  • Retrofit实现图文上传至服务器

    前言:现在大多数的项目中都涉及图片+文字上传了,下面请详见实现原理: 开发环境:AndroidStudio 1.引入依赖: compile 'com.squareup.retrofit2:retrofit:2.1.0'   2.网络权限: <uses-permission android:name="android.permission.INTERNET" />   3.创建上传对象OkHttpClient : private static final OkHttpClie

  • Retrofit 源码分析初探

    现如今,Android开发中,网络层Retrofit+Okhttp组合好像已成标配,身为技术人员,这么火的框架当然得一探究竟,不为装逼,纯粹是为了充电而已. 基本使用介绍 介绍源码前,我们先看下Retrofit的基本使用,大致了解下流程,跟着这个流程来分析源码才不会乱. 1.初始化Retrofit对象 Retrofit retrofit = new Retrofit.Builder() //使用自定义的mGsonConverterFactory .addConverterFactory(Gson

  • Android Retrofit的简单介绍和使用

    Retrofit与okhttp共同出自于Square公司,retrofit就是对okhttp做了一层封装.把网络请求都交给给了Okhttp,我们只需要通过简单的配置就能使用retrofit来进行网络请求了,其主要作者是Android大神JakeWharton. 导包: compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'//Retrofit2所需要的包 compile 'com.squareup.retrofit2:converter-gso

  • 简略分析Android的Retrofit应用开发框架源码

    面对一个项目,对于Android应用开发框架的选择,我想过三种方案: 1.使用Loader + HttpClient + GreenDao + Gson + Fragment,优点是可定制性强,由于使用Google家自己的Loader和LoaderManager,代码健壮性强. 缺点是整套代码学习成本较高,使用过程中样板代码较多,(比如每一个Request都需要产生一个新类) 2.Volley,作为Google在IO大会上得瑟过的一个网络库,其实不算什么新东西(2013 IO发布),使用较为简单

  • Android Retrofit的使用详解

    关于Retrofit的学习,我算是比较晚的了,而现在Retrofit已经是Android非常流行的网络请求框架了.之前,我没有学过Retrofit,但最近公司的新项目使用了Retrofit.Rxjava和OkHttp来进行封装,使用起来非常简便,增加代码的美观程度,也降低了耦合度,这是一个非常棒的框架,特别是这三者一起使用. 简介 Retrofit是Square公司开发的一款针对Android网络请求的框架,现在已经更新到2.3版本了.Retrofit的最大特点是使用运行时注解的方式提供功能.

  • Android网络请求框架Retrofit详解

    介绍: Retrofit 是Square公司开发的一款针对Android网络请求的框架,Retrofit2底层基于OkHttp实现的,OkHttp现在已经得到Google官方认可,大量的app都采用OkHttp做网络请求.本文使用Retrofit2.0.0版本进行实例演示. 使用Retrofit可以进行GET,POST,PUT,DELETE等请求方式. 同步请求:需要在子线程中完成,会阻塞主线程. Response response = call.execute().body(); 异步请求:请

  • 详解Retrofit2.0 公共参数(固定参数)

    本文主要介绍了Retrofit2.0 公共参数(固定参数),分享给大家,具体如下: 请先阅读: Retrofit 动态参数(非固定参数.非必须参数)(Get.Post请求) 在实际项目中,对于有需要统一进行公共参数添加的网络请求,可以使用下面的代码来实现: RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(ctx).setRequestInterceptor(new RequestInterceptor() { @O

  • 详解vue-router2.0动态路由获取参数

    一下demo演示2.0中的vue-router是如何获取到不同参数的,并在地址栏中匹配不同的信息 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript" src="vue.js&qu

  • 详解Go语言Slice作为函数参数的使用

    目录 前言 问题与解析 典型问题 其它疑问1 其它疑问2 结论 参考链接 前言 首先要明确Go语言中实质只有值传递,引用传递和指针传递是相对于参数类型来说. 个人认为上诉的结论不对,把引用类型看做对指针的封装,一般封装为结构体,结构体是值类型,所以感觉都是值传递.不然我感觉其它语言实质不也都是值传递?不过我刚学Go,可能还没完全弄懂,这个有问题可以互相讨论下. Go语言中的值类型:int.float.bool.array.sturct等,声明一个值类型变量时,编译器会在栈中分配一个空间,空间里存

  • 详解C++ sort函数的cmp参数

    目录 1.升序排序 2.降序排序 3.结构体的排序实例 前言: 学算法的第一天你在学冒泡.桶排 在你还没搞明白快排和归并的时候 你已经学到了数据结构最后的堆排序和希尔排序 可以说排序是很多竞赛生的噩梦-- 于是它诞生了 void std::sort() Sort the elements of a sequence using a predicate for comparison. 参数: __first – An iterator. __last – Another iterator. __c

  • 详解如何在SpringBoot中自定义参数解析器

    目录 前言 1.自定义参数解析器 2.PrincipalMethodArgumentResolver 3.RequestParamMapMethodArgumentResolver 4.小结 前言 在一个 Web 请求中,参数我们无非就是放在地址栏或者请求体中,个别请求可能放在请求头中. 放在地址栏中,我们可以通过如下方式获取参数: String javaboy = request.getParameter("name "); 放在请求体中,如果是 key/value 形式,我们可以通

  • 详解numpy.ndarray.reshape()函数的参数问题

    我们知道numpy.ndarray.reshape()是用来改变numpy数组的形状的,但是它的参数会有一些特殊的用法,这里我们进一步说明一下.代码如下: import numpy as np class Debug: def __init__(self): self.array1 = np.ones(6) def mainProgram(self): print("The value of array1 is: ") print(self.array1) print("Th

  • 详解MySQL8.0 密码过期策略

    MySQL8.0.16开始,可以设置密码的过期策略,今天针对这个小的知识点进行展开. 1.手工设置单个密码过期 MySQL8.0中,我们可以使用alter user这个命令来让密码过期. 首先我们创建账号yeyz,密码是yeyz [root@VM-0-14-centos ~]# /usr/local/mysql-8.0.19-el7-x86_64/bin/mysql -uyeyz -pyeyz -h127.0.0.1 -P4306 -e "select 1" mysql: [Warni

  • 详解Vue3.0 + TypeScript + Vite初体验

    项目创建 npm: $ npm init vite-app <project-name> $ cd <project-name> $ npm install $ npm run dev or yarn: $ yarn create vite-app <project-name> $ cd <project-name> $ yarn $ yarn dev 项目结构 main.js 在个人想法上,我觉得createApp()是vue应用的实例,createApp

  • 详解MySQL8.0原子DDL语法

    01 原子DDL介绍 原子DDL语句将数据字典更新.存储引擎操作和与DDL操作相关联的二进制日志写入合并到单个原子操作中.该操作要么提交,对数据字典.存储引擎和二进制日志保留适用的更改,要么回滚. 在MySQL8.0中,原子DDL操作这一特性,支持表相关操作,例如create table.drop table等,也支持非表相关操作,例如create routine.drop trigger等. 其中: 支持的表操作包含: drop.create.alter(操作对象是databases, tab

  • 详解TypeScript2.0标记联合类型

    目录 使用标记的联合类型构建付款方式 使用标记联合类型构建 Redux 操作 never 类型 永不返回的函数 不可能有该类型的变量 never 和 void 之间的区别 函数声明的类型推断 使用标记的联合类型构建付款方式 假设咱们为系统用户可以选择的以下支付方式建模 Cash (现金) PayPal 与给定的电子邮件地址 Credit card 带有给定卡号和安全码 对于这些支付方法,咱们可以创建一个 TypeScript 接口 interface Cash { kind: "cash&quo

随机推荐