我正在使用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
。
有人知道怎么做吗?
答案 0 :(得分:6)
这很容易。下面的代码使用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。输出:
真
答案 1 :(得分:0)
另一种方法是添加Filter并使用包装器修改请求/响应对象。
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代理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);
}