Retrofit掉坑记录(一)

随着项目的深入,Retrofit会深入学习,本系列应该当入坑先驱了,不说了,都是泪【累】

1.@QueryMap AND @FieldMap 错用将会导致HTTP 414错误
首先借用HTTP 414的某百科解释 :
您的 Web 服务器认为,客户端(如您的浏览器或我们的 CheckUpDown 机器人)发送的 HTTP 数据流包含一个过长网址, 即字节太多。
相信了解了414解释的,就知道我错误在哪里吧,项目实例介绍如下:
有如下表单:

<H4>用户信息编辑</H4>
    <form action="action.do.php" method="post" name="form_useredit" enctype="multipart/form-data">
    用户ID:<input type="text" name="uid" value='2'"><br>
    <input type="hidden" name="Platformtype" value="Android">
    <input type="hidden" name="enctype" value="post">
    用户名:<input type="text" name="username" value=''"><br>
    性别:<input type="radio" name="sex" value="1" checked>男  
          <input type="radio" name="sex" value="0">女<br>
    出生日期:<input type="date" name="birthday" value="1980-11-1"><br>
    所在地(省):<input type="text" name="province" value=""><br>
    所在地(市):<input type="text" name="location" value=""><br>
    签名<input type="text" name="signature" value=""><br>
    <input type="file" name="headico">
    <input type="text" name="action" value="user_user_editdo">
    <input type="submit" name="submit1" value="修改" />
</form>

这是一个简单的HTTP表单请求,Retrofit与RxJava结合如下:
1.RetrofitUtils构建:

 public static <T> T createService(Class<T> clazz) {
        if (retrofit == null) {
            synchronized (RetrofitUtil.class) {
                Retrofit.Builder builder = new Retrofit.Builder();
                retrofit = builder.baseUrl(Constant.BASE_IP)
                        .client(okHttpClient)
                        .addConverterFactory(gsonConverterFactory)
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .build();
            }
        }
        return retrofit.create(clazz);
    }

创建请求接口:

/**
     * 用户信息上传
     * @param options
     * @param externalFileParameters
     * @return
     */
    @POST(Constant.BASE_ACTION)
    @Multipart
    Observable<ResponseBody> uploadProduct1(@QueryMap Map<String,RequestBody> options ,
                                      @PartMap Map<String, RequestBody> externalFileParameters) ;

/**
     * 用户信息上传
     * @param options
     * @param externalFileParameters
     * @return
     */
    @POST(Constant.BASE_ACTION)
    @Multipart
    Observable<ResponseBody> uploadProduct2(@PartMap Map<String,RequestBody> options,
                                      @PartMap Map<String, RequestBody> externalFileParameters) ;

刚开始使用方式1上传,Map构建如下:

Map<String,String> optionMap = new HashMap<String,String>();
optionMap.put("username",username);
optionMap.put("sex",sex);
optionMap.put("age",age);
.....//省略

刚开始没发现什么问题,但是后来在极限测试的情况下(用户的自我介绍是一个非常长的字符串,到底有多长,我得无问问测试的小MM了),会出现HTTP414 的问题,是bug就要改的啊,所以就抓包看来一下,发现如果是@QueryMap封装的参数,这个Map中的值最终都会拼接在URL后面,就像这样:http://www.exmaple.com?username=zhangsan&age=18&desc=shejklfajfkljeflkejfkejfejf&location=shanghai&gendar=1 …… 所以这样就不对了,在查看了相关的HTTP资料,发现网络请求的URI的长度有长度的限制,这个与浏览器的有关系,不同的浏览器限制的长度会不一致 。最后发现了Retrofit2.0中还有一个@FieldMap,这个参数就是给我们的表单提交所使用的,我们是使用的是POST请求,使用FieldMap会将请求的参数封装在Request的Body中,理论上POST请求是没有长度限制的。
使用FieldMap的请求如下:

//对于字符串的key=value,需要封装为:
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), value)
//对于存在文的form表单
RequestBody requestBody = RequestBody.create(MediaType.parser("multipart/form-data",fileName);

Map<String,RequestBody > optionMap = new HashMap<String,RequestBody >() ;
optionMap.put("username",RequestBody.create(MediaType.parse("text/plain"), username);
optionMap.put("location",RequestBody.create(MediaType.parse("text/plain"), location);
optionMap.put("age",RequestBody.create(MediaType.parse("text/plain"), age);
optionMap.put("gendar",RequestBody.create(MediaType.parse("text/plain"), gendar);
//存在文件时:
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), tempFile);
optionMap.put("filename=\"" + tempFile.getName() + "", requestBody);

2.Retrofit请求乱码
作为Android开发者,UTF-8字符编码已经成为我们的共识,所以在server端+client配合的很好的情况下,一般不会出现这种问题,但是蛋疼的我,就还是如愿的出现了。最近很闲的时候,在android访问 泡网,打算写个客户端可以看看这个很好的一个学习网站,网络请求就是Retrofit+Ok3,真是当你用够了aFinal/XUtils/Volley/asynchttp之后,我会发现Retrofit真好,简单好用。。。
但是坑爹的是,这个网站居然是:
这里写图片描述
我猜写这个网站的家伙非常爱国吧,其他的真没什么好说的,不过生活还是要继续啊,按照平常的写法,返回的数据是这样的:
这里写图片描述
是很标准的乱码,思考了一下,写了个比较古老的方法,我感觉肯定还有更简单的只是没想到罢了,由于时间关系,就没有多想,方法如下:

 public static <T> T getGB2312Instance(String baseURL, Class<T> clazz) {
        Retrofit.Builder builder;
        if (null == (builder = mContainer.get(baseURL))) {
            synchronized (RetrofitUtils.class) {
                builder = new Retrofit.Builder();
                builder.client(getGB2312Client()).
                        addCallAdapterFactory(RxJavaCallAdapterFactory.create()).
                        addConverterFactory(InputStreamConvertFactory.create());

                mContainer.put(baseURL, builder);
            }
        }
        return builder.baseUrl(baseURL).build().create(clazz);
    }

这里添加了一个InputStreamConvertFactory,代码如下:

public class InputStreamConvertFactory extends Converter.Factory {

    public static InputStreamConvertFactory create() {
        return new InputStreamConvertFactory();
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return new InputStreamResponseConverter<String>();
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        return new InputStreamRequestConverter<String>();
    }
}

其中InputStreamResponseConverter如下:

public class InputStreamResponseConverter<T> implements Converter<ResponseBody,T> {

    InputStreamResponseConverter() {}

    /**
    主要思路为我需要Retrofit返回InputStream,然后使用GB2312进行转码得到我们想要的编码字符串
    */  
    @Override
    public T convert(ResponseBody value) throws IOException {
        return (T) (new String(value.bytes(),"GB2312"));
    }
}

而相对不是特别重要的InputStreamRequestConverter代码如下:

public class InputStreamRequestConverter<T> implements Converter<T, RequestBody> {

    private static final MediaType MEDIA_TYPE = MediaType.parse("text/html; charset=UTF-8");

    /**
     这是是抄StringConverterFactory中的源码,因为我们不需要对要请求的参数进行编码
    */  
    @Override
    public RequestBody convert(T value) throws IOException {
        return RequestBody.create(MEDIA_TYPE, value.toString());
    }
}

今晚就记录两个坑了,以后还有更多,本菜鸟也是刚刚学Retrofit,有些不对的不好的,希望大家指出,共同进步。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值