标签:否则 sign cal for utf-8 values content rpo nal
requestBodyConverter 不执行的解决办法:
参数要使用@Body这种形式,否则 request 方法会不起作用。
在Retrofit中,无论是发送数据和接收数据,都是通过OKHttp的RequestBody和ResponseBody来实现的。在实际项目中,有时候原始的RequestBody或是ResponseBody并不能满足我们的需求(如接口加密),就需要对它进行转换。
在Retrofit通过build()方法获得实例时,可以添加多个ConverterFactory,但要注意的是,添加的顺序是有影响的。如下代码:
.addConverterFactory(GsonConverterFactory.create())
看下源码:
private final List<Converter.Factory> converterFactories;
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
按照retrofit的逻辑,是从前往后进行匹配,如果匹配上,就忽略后面的,直接使用。
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter.Factory factory = converterFactories.get(i);
Converter<?, RequestBody> converter =
factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<T, RequestBody>) converter;
}
}
从上面的源码中可以看到,当factory.requestBodyConverter返回空时,表示没有匹配上,可使用下一个factory.
因此,当我们自定义converter的时候,需要进行条件判断,符合我们一定规则的才能使用。
Retrofit官方给了以下几个常用的转换库
我们以创建protobuff为例。
Retrofit已经为我们提供了自定义ConverterFactory的接口,我们只需要实现它给的接口即可。
public final class ProtoConverterFactory extends Converter.Factory {
public static ProtoConverterFactory create() {
return new ProtoConverterFactory();
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
//进行条件判断,如果传进来的Type不是class,则匹配失败
if (!(type instanceof Class<?>)) {
return null;
}
//进行条件判断,如果传进来的Type不是MessageLite的实现类,则也匹配失败
Class<?> c = (Class<?>) type;
if (!MessageLite.class.isAssignableFrom(c)) {
return null;
}
Parser<MessageLite> parser;
try {
Field field = c.getDeclaredField("PARSER");
//noinspection unchecked
parser = (Parser<MessageLite>) field.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalArgumentException(
"Found a protobuf message but " + c.getName() + " had no PARSER field.");
}
//返回一个实现了Converter的类,
return new ProtoResponseBodyConverter<>(parser);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
//进行条件判断,如果传进来的Type不是class,则匹配失败
if (!(type instanceof Class<?>)) {
return null;
}
//进行条件判断,如果传进来的Type不是MessageLite的实现类,则也匹配失败
if (!MessageLite.class.isAssignableFrom((Class<?>) type)) {
return null;
}
return new ProtoRequestBodyConverter<>();
}
}
注意在Convert的两个泛型中,前一个类型是传进来的对象类型,后一个类型是转换后的对象类型。
final class ProtoRequestBodyConverter<T extends MessageLite> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/x-protobuf");
@Override public RequestBody convert(T value) throws IOException {
byte[] bytes = value.toByteArray();
return RequestBody.create(MEDIA_TYPE, bytes);
}
}
final class ProtoResponseBodyConverter<T extends MessageLite>
implements Converter<ResponseBody, T> {
private final Parser<T> parser;
ProtoResponseBodyConverter(Parser<T> parser) {
this.parser = parser;
}
@Override public T convert(ResponseBody value) throws IOException {
try {
return parser.parseFrom(value.byteStream());
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e); // Despite extending IOException, this is data mismatch.
} finally {
value.close();
}
}
}
在有一些情况,仅仅通过type来进行判断,信息是不够的,还需要额外的参数。这时我们就可以利用后面两个参数来进行。
我们先看requestBodyConverter的函数签名。
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit)
显然,parameterAnnoatations是指在定义接口的参数上的注解。如下面的MyType:
@GET("applist/mini-appcenter/")
Call<MiniAppCenterPojo> getMiniApp(@Query("offsets") @TypeString String offsets);
定义在方法上的注释就是methodAnnotations
@TypeString
@GET("applist/mini-appcenter/")
Call<MiniAppCenterPojo> getMiniApp(@Query("offsets") String offsets);
我们就可以通过对这些注解的判断来进行自定义Converter的匹配。
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface TypeString{
}
public class StringConverterFactory extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (!(type instanceof Class<?>)) {
return null;
}
for( Annotation annotation :annotations) {
if( annotation instanceof TypeString) {
return new StringResponseConverter();
}
}
return null;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (!(type instanceof Class<?>)) {
return null;
}
for( Annotation annotation :parameterAnnotations) {
if( annotation instanceof TypeString) {
return new StringRequestConverter();
}
}
return null;
}
public static class StringResponseConverter implements Converter<ResponseBody, String> {
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
}
public static class StringRequestConverter implements Converter<String, RequestBody> {
@Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MediaType.parse("application/octet-stream"), value);
}
}
}
下面再看一个基于Gson的自定义ConverterFactory。
如果我们对安全性要求比较高,或者编码不太一样的话,默认的GsonConverterFactory就不行了,我们就需要自定义ConverterFactory。
代码如下:
public final class DecodeConverterFactory extends Converter.Factory {
public static DecodeConverterFactory create() {
return create(new Gson());
}
public static DecodeConverterFactory create(Gson gson) {
return new DecodeConverterFactory(gson);
}
private final Gson gson;
private DecodeConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new DecodeResponseBodyConverter<>(adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new DecodeRequestBodyConverter<>(gson, adapter);
}
}
然后我们需要自定义responseBodyConverter和requestBodyConverter,这里我们发送请求的时候不需要加密,接收请求的时候需要解密,具体代码如下:
public class DecodeResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final TypeAdapter<T> adapter;
DecodeResponseBodyConverter(TypeAdapter<T> adapter) {
this.adapter = adapter;
}
@Override
public T convert(ResponseBody value) throws IOException {
//解密字符串
return adapter.fromJson(EncryptUtils.decode(value.string()));
}
}
public class DecodeRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
DecodeRequestBodyConverter(Gson gson,TypeAdapter<T> adapter){
this.gson = gson;
this.adapter = adapter;
}
@Override
public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(),UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter,value);
jsonWriter.flush();
return RequestBody.create(MEDIA_TYPE,buffer.readByteString());
}
}
需要注意的是:adapter.fromJson(EncryptUtils.decode(value.string())) 中EncryptUtils.decode(value.string())返回类型必须是json字符串,否则会报错。
可能遇到的问题:
when required parameters have string and file together, retrofit 2.2.0 do not work
requestBodyConverter 不执行的解决办法:
参数要使用@Body这种形式,否则 request 方法会不起作用。
例如:
@POST("/index/index/sms_code")
Observable<BaseResopnse> sms_code(@Body String data);
下面我们看下requestBodyConverter的源码:
/**
* Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for types
* specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}
* values.
*/
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}从这里我们可以看到,除了@Body,@Part、@PartMap类型的参数也是可以的。
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow
标签:否则 sign cal for utf-8 values content rpo nal
原文地址:https://www.cnblogs.com/skiwnchhw/p/10349660.html