【问题标题】:Retrofit2: Modifying request body in OkHttp InterceptorRetrofit2:在 OkHttp 拦截器中修改请求正文
【发布时间】:2016-04-19 21:02:32
【问题描述】:

我在 Android 应用程序中使用带有 OkHttp 客户端的 Retrofit 2 (2.0.0-beta3),到目前为止一切顺利。但目前我正面临 OkHttp 拦截器的问题。我正在与之通信的服务器正在请求正文中获取访问令牌,因此当我拦截添加身份验证令牌的请求或在需要添加更新的身份验证令牌时在 Authenticator 的身份验证方法中时,我需要为此修改请求正文.但看起来我只能在标头中添加数据,而不能在正在进行的请求的正文中添加数据。目前我写的代码如下:

client.interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                if (UserPreferences.ACCESS_TOKEN != null) {
                    // need to add this access token in request body as encoded form field instead of header
                    request = request.newBuilder()
                            .header("access_token", UserPreferences.ACCESS_TOKEN))
                            .method(request.method(), request.body())
                            .build();
                }
                Response response = chain.proceed(request);
                return response;
            }
        });

谁能指出我正确的方向,即如何修改请求正文以添加我的访问令牌(第一次或在令牌刷新后更新)?任何指向正确方向的指针将不胜感激。

【问题讨论】:

    标签: android retrofit okhttp


    【解决方案1】:

    我使用它来将 post 参数添加到现有的。

     OkHttpClient client = new OkHttpClient.Builder()
                        .protocols(protocols)
                        .addInterceptor(new Interceptor() {
                            @Override
                            public Response intercept(Chain chain) throws IOException {
                                Request request = chain.request();
                                Request.Builder requestBuilder = request.newBuilder();
    RequestBody formBody = new FormEncodingBuilder()
                .add("email", "Jurassic@Park.com")
                .add("tel", "90301171XX")
                .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);
                            }
                        })
                        .build();
    
    public 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";
            }
        }
    

    OkHttp3:

    RequestBody formBody = new FormBody.Builder()
                    .add("email", "Jurassic@Park.com")
                    .add("tel", "90301171XX")
                    .build();
    

    【讨论】:

    【解决方案2】:

    我将使用 Dagger 分享我的 @Fabian 答案的 Kotlin 实现。我希望将origin=app 添加到 GET 请求的请求 url,并添加到表单编码的 POST 请求的正文中

    @Provides
    @Singleton
    fun providesRequestInterceptor() =
            Interceptor {
                val request = it.request()
    
                it.proceed(when (request.method()) {
                    "GET" -> {
                        val url = request.url()
                        request.newBuilder()
                                .url(url.newBuilder()
                                        .addQueryParameter("origin", "app")
                                        .build())
                                .build()
                    }
                    "POST" -> {
                        val body = request.body()
                        request.newBuilder()
                                .post(RequestBody.create(body?.contentType(),
                                        body.bodyToString() + "&origin=app"))
                                .build()
                    }
                    else -> request
                })
            }
    
    fun RequestBody?.bodyToString(): String {
        if (this == null) return ""
        val buffer = okio.Buffer()
        writeTo(buffer)
        return buffer.readUtf8()
    }
    

    【讨论】:

      【解决方案3】:

      因为这不能写在@Fabian 上一个答案的 cmets 中,所以我将这个作为单独的答案发布。这个答案既涉及“应用程序/json”,也涉及表单数据。

      import android.content.Context;
      
      import org.json.JSONException;
      import org.json.JSONObject;
      
      import java.io.IOException;
      
      import okhttp3.FormBody;
      import okhttp3.Interceptor;
      import okhttp3.MediaType;
      import okhttp3.Request;
      import okhttp3.RequestBody;
      import okhttp3.Response;
      import okio.Buffer;
      
      /**
       * Created by debanjan on 16/4/17.
       */
      
      public class TokenInterceptor implements Interceptor {
          private Context context; //This is here because I needed it for some other cause 
      
          //private static final String TOKEN_IDENTIFIER = "token_id";
          public TokenInterceptor(Context context) {
              this.context = context;
          }
      
          @Override
          public Response intercept(Chain chain) throws IOException {
              Request request = chain.request();
              RequestBody requestBody = request.body();
              String token = "toku";//whatever or however you get it.
              String subtype = requestBody.contentType().subtype();
              if(subtype.contains("json")){
                  requestBody = processApplicationJsonRequestBody(requestBody, token);
              }
              else if(subtype.contains("form")){
                  requestBody = processFormDataRequestBody(requestBody, token);
              }
              if(requestBody != null) {
                  Request.Builder requestBuilder = request.newBuilder();
                  request = requestBuilder
                          .post(requestBody)
                          .build();
              }
      
              return chain.proceed(request);
          }
          private 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";
              }
          }
          private RequestBody processApplicationJsonRequestBody(RequestBody requestBody,String token){
              String customReq = bodyToString(requestBody);
              try {
                  JSONObject obj = new JSONObject(customReq);
                  obj.put("token", token);
                  return RequestBody.create(requestBody.contentType(), obj.toString());
              } catch (JSONException e) {
                  e.printStackTrace();
              }
              return null;
          }
          private RequestBody processFormDataRequestBody(RequestBody requestBody, String token){
              RequestBody formBody = new FormBody.Builder()
                      .add("token", token)
                      .build();
              String postBodyString = bodyToString(requestBody);
              postBodyString += ((postBodyString.length() > 0) ? "&" : "") +  bodyToString(formBody);
              return RequestBody.create(requestBody.contentType(), postBodyString);
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        您可以通过以下方法编辑请求正文,传递请求和参数进行编辑。

        private fun editBody(request: Request, parameter: String): RequestBody {
                   
          val oldBody = request.body //retrieve the current request body
          val buffer =  Buffer()
          oldBody?.writeTo(buffer)
        
          val strOldBody = buffer.readUtf8() // String representation of the current request body
          buffer.clear()
          buffer.close()
        
          val strNewBody = JSONObject(strOldBody).put("parameter", parameter).toString()
          return strNewBody.toRequestBody(request.body?.contentType()) // New request body with the encrypted/modified string of the current request body
        
        }
        

        现在您可以使用更新的请求正文再次请求

        override fun intercept(chain: Interceptor.Chain): Response {
              val request: Request = chain.request()
              return chain.proceed(requestWithUpdatedParameter(request, "parameter"))
            
        }
            
        private fun requestWithUpdatedParameter(req: Request, parameter: String): Request {
                        val newRequest: Request
                        val body = editBody(req, parameter)
                        newRequest = req.newBuilder().method(req.method, body).build()
                        return newRequest
        }
        

        【讨论】:

          【解决方案5】:
          private static class NetworkInterceptor implements Interceptor {
          
          @Override
          public Response intercept(Chain chain) throws IOException {
              
              Request request = chain.request();
              RequestBody oldBody = request.body(); //retrieve the current request body
              Buffer buffer = new Buffer();
              oldBody.writeTo(buffer);
              String strOldBody = buffer.readUtf8(); // String representation of the current request body
              buffer.clear();
              buffer.close();
          
              MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
              String strNewBody = enDecService.encryptBody(strOldBody); // Your encryption/ modification logic 
              RequestBody body = RequestBody.create(mediaType, strNewBody); // New request body with the encrypted/modified string of the current request body
          
              request = request.newBuilder()
                      .header("Content-Type", "application/json")
                      .header("Content-Length", String.valueOf(body.contentLength()))
                      .header("Authorization", "Bearer " + "your token")
                      .method(request.method(), body).build();
          
          
              long t1 = System.nanoTime();
              Log.d(TAG, String.format("Sending request %s on %s", request.url(), request.headers()));
          
              Response response = chain.proceed(request); // sending req. to server. current req. body is a encrypted string.
              int maxAge = 6000; // read from cache for 6000 seconds even if there is internet connection
              response.header("Cache-Control", "public, max-age=" + maxAge);
              response = response.newBuilder().removeHeader("Pragma").build();
          
          
              long t2 = System.nanoTime();
              Log.d(TAG, String.format("Received response for %s in %.1fms  %s", response.request().url(), (t2 - t1) / 1e6d, response.toString()));
          
              try {
                  String s = response.body().string(); // retrieve string representation of encrypted response assuming your response is encrypted.
                  ResponseBody responseBody = ResponseBody.create(mediaType, enDecService.decryptBody(s)); // decrypt the encrypted response or make other modifications.yor decryption/modifications logic goes here.
                  response = response.newBuilder().body(responseBody).build(); // build a new response with the decrypted response body.
              } catch (JOSEException e) {
          
              } catch (ParseException e) {
          
              }
              return response;
          }
          

          }

          【讨论】:

          • 您能在您的代码中添加解释吗?
          • 如您所见,我使用了 OkHttp 拦截器。在拦截(链链)方法中,您可以获得原始请求正文,但要对其进行操作,您必须将其转换为字符串对象(使用缓冲区)作为改造请求类不提供任何操作方法。然后,您可以操作(如加密)字符串对象(通过使用序列化或 Gson 将其转换为模型类)。使用操作的 String 对象作为 req 创建一个新的 Request 对象。新请求对象的主体然后转发该请求。谢谢。
          【解决方案6】:

          我正在使用这种方式来验证我的令牌

          final OkHttpClient okHttpClient = new OkHttpClient.Builder()
                          .connectTimeout(30, TimeUnit.SECONDS) //retrofit default 10 seconds
                          .writeTimeout(30, TimeUnit.SECONDS) //retrofit default 10 seconds
                          .readTimeout(30, TimeUnit.SECONDS) //retrofit default 10 seconds
                          .addInterceptor(logging.setLevel(HttpLoggingInterceptor.Level.BODY))
                          .addInterceptor(new BasicAuthInterceptor())
                          .build();
          

          这里我通过 BasicAuthInterceptor 发送令牌

          public class MyServiceInterceptor implements Interceptor {
          
          private String HEADER_NAME="Authorization";
          private String OBJECT_NAME="Bearer";
          private String SPACE="  ";
          @Override public Response intercept(Chain chain) throws IOException {
              Request request = chain.request();
          
              Request.Builder requestBuilder = request.newBuilder();
          
              String token= PreferenceManager.getInstance().getString(PreferenceManager.TOKEN);
                  if (token != null) { {
                      requestBuilder.addHeader(HEADER_NAME, OBJECT_NAME+SPACE+ token);
                  }
          }
          
              return chain.proceed(requestBuilder.build());
          

          } }

          【讨论】:

          • 这与修改body无关
          猜你喜欢
          • 1970-01-01
          • 2019-03-03
          • 1970-01-01
          • 2021-11-02
          • 1970-01-01
          • 2023-03-11
          • 2018-12-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多