【问题标题】:Modify response body retrofit 2.2 interceptor修改响应体改造 2.2 拦截器
【发布时间】:2018-06-19 04:11:50
【问题描述】:

我正在开发一个使用 Retrofit 2 来请求 API 的应用程序。此 API 在 ASP.NET 中,它使用 GZip 压缩并编码为 Base64,如下面的代码:

private static string Compress(string conteudo)
{
    Encoding encoding = Encoding.UTF8;
    byte[] raw = encoding.GetBytes(conteudo);

    using (var memory = new MemoryStream())
    {
        using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true))
        {
            gzip.Write(raw, 0, raw.Length);
        }
        return Convert.ToBase64String(memory.ToArray());
    }
}

private static string Decompress(string conteudo)
{
    Encoding encoding = Encoding.UTF8;
    var gzip = Convert.FromBase64String(conteudo);

    using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
    {
        int size = gzip.Length;
        byte[] buffer = new byte[size];
        using (MemoryStream memory = new MemoryStream())
        {
            int count = 0;
            do
            {
                count = stream.Read(buffer, 0, size);
                if (count > 0)
                {
                    memory.Write(buffer, 0, count);
                }
            }
            while (count > 0);
            return encoding.GetString(memory.ToArray());
        }
    }
}

现在,我需要在我的 Android 应用程序中获取来自 Retrofit 的响应,从 Base64 解码并解压缩它。我尝试使用Interceptor 进行操作,但没有成功。

这是我从服务H4sIAAAAAAAEACspKk0FAI1M/P0EAAAA收到的返回,解码并解压缩响应,我们有true

有人知道怎么做吗?

【问题讨论】:

    标签: java base64 gzip retrofit interceptor


    【解决方案1】:

    这很容易。下面的代码使用 Google Guava 解码 Base64 字符流,使用 Google Gson 反序列化 JSON 内容。

    考虑如下测试服务接口:

    interface IService {
    
        @GET("/")
        Call<String> get();
    
    }
    

    现在您可以使用模板方法设计模式实现您的拦截器响应输入流转换器库:

    abstract class AbstractTransformingDecodingInterceptor
            implements Interceptor {
    
        protected abstract InputStream transformInputStream(InputStream inputStream)
                throws IOException;
    
        @Override
        @SuppressWarnings("resource")
        public final Response intercept(final Chain chain)
                throws IOException {
            final Request request = chain.request();
            final Response response = chain.proceed(request);
            final ResponseBody body = response.body();
            return response.newBuilder()
                    .body(ResponseBody.create(
                            body.contentType(),
                            body.contentLength(),
                            Okio.buffer(Okio.source(transformInputStream(body.byteStream())))
                    ))
                    .build();
        }
    
    }
    

    此实现还应检测内容 MIME 类型,以免进行错误的转换,但您可以轻松地自行实现。所以这里还有两个用于 Base64 和 GZip 的转换拦截器:

    final class Base64DecodingInterceptor
            extends AbstractTransformingDecodingInterceptor {
    
        private static final Interceptor base64DecodingInterceptor = new Base64DecodingInterceptor();
    
        private Base64DecodingInterceptor() {
        }
    
        static Interceptor getBase64DecodingInterceptor() {
            return base64DecodingInterceptor;
        }
    
        @Override
        protected InputStream transformInputStream(final InputStream inputStream) {
            return BaseEncoding.base64().decodingStream(new InputStreamReader(inputStream));
        }
    
    }
    
    final class GzipDecodingInterceptor
            extends AbstractTransformingDecodingInterceptor {
    
        private static final Interceptor gzipDecodingInterceptor = new GzipDecodingInterceptor();
    
        private GzipDecodingInterceptor() {
        }
    
        static Interceptor getGzipDecodingInterceptor() {
            return gzipDecodingInterceptor;
        }
    
        @Override
        protected InputStream transformInputStream(final InputStream inputStream)
                throws IOException {
            return new GZIPInputStream(inputStream);
        }
    
    }
    

    并对其进行测试:

    private static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(getGzipDecodingInterceptor())
            .addInterceptor(getBase64DecodingInterceptor())
            .addInterceptor(getFakeContentInterceptor())
            .build();
    
    private static final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://whatever")
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    
    private static final IService service = retrofit.create(IService.class);
    
    public static void main(final String... args)
            throws IOException {
        final String body = service.get().execute().body();
        System.out.println(body);
    }
    

    请注意,getFakeContentInterceptor 返回一个始终返回 H4sIAAAAAAAEACspKk0FAI1M/P0EAAAA 的假拦截器,因此 baseUrl 甚至没有真正的 URL。输出:

    是的

    【讨论】:

    • 再一次你帮了我很多,非常感谢:D
    • @LyubomyrShaydariv 嗨,“getFakeContentInterceptor()”在哪里。你能发帖吗?谢谢
    • 这很容易......谢谢
    • 嗨,“BaseEncoding.base64()”在哪里。 ?
    【解决方案2】:

    另一种方法是添加过滤器并使用包装器修改请求/响应对象。

      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
          throws IOException, ServletException {
        // We need to convert the ServletRequest to MultiReadRequest, so that we could intercept later
        MultiReadHttpServletRequest multiReadRequest =
            new MultiReadHttpServletRequest((HttpServletRequest) request);
        HttpServletResponseWrapper responseWrapper =
            new HttpServletResponseWrapper((HttpServletResponse) response);
        chain.doFilter(multiReadRequest, responseWrapper);
      }
    

    您可以添加代理层(jetty proxy servlet org.eclipse.jetty.proxy.ProxyServlet)并覆盖方法:onResponseContent、addProxyHeaders

     public void handleProxyResponse(
          HttpServletRequest request,
          HttpServletResponse response,
          byte[] buffer,
          int offset,
          int length,
          Callback callback) {
        try {
          if (response.getStatus() == HttpStatus.OK_200
              && request.getRequestURI().startsWith(INTERCEPTED_END_POINT)) {
            String output;
            boolean acceptsGZipEncoding = acceptsGZipEncoding(request);
            if (acceptsGZipEncoding) {
              output = plainTextFromGz(buffer);
            } else {
              output = new String(buffer);
            }
            String proxyHost = getRequestUrlBase(request);
            try {
              // TODO: get this from config object
              output = output.replace(ProxyServer.REMOTE_HOST, proxyHost);
              byte[] outBuffer;
              if (acceptsGZipEncoding) {
                outBuffer = gzFromPlainText(output);
              } else {
                outBuffer = output.getBytes();
              }
              HttpServletResponseWrapper responseWrapper = (HttpServletResponseWrapper) response;
              responseWrapper.getResponse().reset();
              responseWrapper.getOutputStream().write(outBuffer);
            } catch (Exception e) {
              log.error(e.getMessage(), e);
              // Error in parsing json, writing original response
              response.getOutputStream().write(buffer, offset, length);
            }
          } else {
            response.getOutputStream().write(buffer, offset, length);
          }
          callback.succeeded();
        } catch (Throwable e) {
          callback.failed(e);
        }
      }
    
      private String getRequestUrlBase(HttpServletRequest request) {
        logHeaders(request);
        return request.getHeader(HttpHeader.HOST.name());
      }
    
      private String replaceBaseFromUrl(String url, String base) throws Exception {
        URI uri = new URI(url);
        StringBuffer sb = new StringBuffer("");
        if (base.startsWith("http")) {
          sb.append(base);
        } else {
          sb.append("http://").append(base);
        }
        sb.append(uri.getPath());
        if (!Strings.isNullOrEmpty(uri.getQuery())) {
          sb.append("?").append(uri.getQuery());
        }
        return sb.toString();
      }
    
      private boolean acceptsGZipEncoding(HttpServletRequest httpRequest) {
        String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
        return acceptEncoding != null && acceptEncoding.indexOf("gzip") != -1;
      }
    
      private byte[] gzFromPlainText(String plainText) {
        if (Strings.isNullOrEmpty(plainText)) {
          return null;
        }
    
        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            final GZIPOutputStream gzipOutput = new GZIPOutputStream(baos)) {
          gzipOutput.write(plainText.getBytes());
          gzipOutput.finish();
          return baos.toByteArray();
        } catch (IOException e) {
          log.error("Could not convert plain text to gz", e);
        }
        return plainText.getBytes();
      }
    
      private String plainTextFromGz(byte[] gz) {
        try (GZIPInputStream gzipIn = new GZIPInputStream(new ByteArrayInputStream(gz))) {
          return IOUtils.toString(gzipIn, Charset.defaultCharset());
        } catch (IOException e) {
          log.error("Could not write gz to plain text", e);
        }
        return new String(gz);
      }
    

    【讨论】:

      猜你喜欢
      • 2016-05-02
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-23
      • 1970-01-01
      相关资源
      最近更新 更多