跳至主要內容

请求参数注解(四)

guodongAndroid大约 4 分钟

前言

在上一篇文章 请求参数注解(三) 中我们学习了 Field 注解,本篇文章我们学习了 Part 相关的注解。

annotation-type

从上图可以看出与 Part 相关的注解有:

  • @Part
  • @PartMap

multipart/form-data

在 Retrofit 中与 @Part 注解相关的是 @Multipart 注解,在 HTTP 中与 @Multipart 注解相关的是 multipart/form-data 编码方式,所以我们先简单学习下 multipart/form-data 相关的知识,来更好的理解 @Part 注解的作用。

multipart/form-data 是表单提交中的一种编码方式,它的定义可以参考 RFC-7578 4.1章节open in new window,简单理解:multipart/form-data 可以包含一个或多个 Part,每个 Part 之间由 boundary 参数分隔。

以下是一个简单的 multipart/form-data 请求示例:

POST /user/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=123456
 
--123456
Content-Disposition: form-data; name="username"
guodongAndroid
--123456
Content-Disposition: form-data; name="file"; filename="水墨西湖.jpg";
<水墨西湖.jpg>
--123456--
  • 第2行:Content-Type 请求头的值是我们设置的 multipart/form-data 编码方式,它有一个必选参数 boundary,用于分隔每个 Part
  • 第3行:空白行,
  • 第4行:Part 的分隔符,格式为:--<boundary>
  • 第5行:Part 部分的必选头字段:Content-Disposition,其值必须为:form-data,必须包含一个 name 的参数,
  • 第6行:Part 部分的内容(body)
  • 第7行:Part 的分隔符,格式为:--<boundary>
  • 第8行:Part 部分的必选头字段:Content-Disposition,其值必须为:form-data,必须包含一个 name 的参数,如果是文件,还可以包含一个 filename 参数,
  • 第9行:文件的二进制数据,此处以文字代替,
  • 第10行:最后一个 Part 结尾符,格式为:--<boundary>--

通过上述示例,对于 Part 我们可以简单理解:就是我们需要传入的数据。

有关 Part 的详细信息可以参考 RFC-2046 5.1章节open in new window

@Part

@Part 注解标记HTTP接口方法中的一个参数,表示这个参数是 Form 表单里的一个 Part

@Part 注解有两个参数,分别为:

  1. value:可选参数,表示 Partname 参数的值,

  2. encoding:可选参数,表示 Part 中的可选头字段 Content-Transfer-Encoding 的值,Retrofit 默认:binary,HTTP协议默认:7-bit,

简单使用示例:

interface Service {
    @Multipart
	@POST("foo/bar")
	Call<ResponseBody> method(@Part("lang") String lang);
}

Service service = retrofit.create(Service.class);
service.method("zh");

@Part 注解使用时有以下几点需要注意:

  1. @Part 注解仅能在被 @Multipart 注解标注的方法中使用,否则抛出异常,

  2. 如果注解标记的参数类型是 okhttp3.MultipartBody.Partvalue 参数的值取自 okhttp3.MultipartBody.Part 中,value 参数不可填充值,否则抛出异常,

    1. 可以标记 okhttp3.MultipartBody.Part 的集合和数组类型,
  3. 如果注解标记的参数类型是其他类型,value 参数必须填充值,否则抛出异常,

    1. 其他类型的参数值会经过 Retrofit#requestBodyConverter(Type, Annotation[], Annotation[]) 转换,
  4. 如果参数值为 null,则会被忽略,

标记参数类型value 是否填充encoding 是否有效
okhttp3.MultipartBody.Part必须为空无效
其他类型必须填充有效

@PartMap

@PartMap 注解标记HTTP接口方法中的一个参数,表示这个参数是 Form 表单里的一个或多个 Part

@PartMap 注解目前只有一个参数:

  1. encoding:可选参数,表示 Part 中的可选头字段 Content-Transfer-Encoding 的值,Retrofit 默认:binary,HTTP协议默认:7-bit,

简单使用示例:

interface Service {
    @Multipart
	@POST("foo/bar")
	Call<ResponseBody> method(@Part("lang") String lang, @PartMap Map<String, String> parts);
}

Service service = retrofit.create(Service.class);
service.method("zh", ImmutableMap.of("foo", "bar", "kit", "kat");

@PartMap 注解使用时有以下几点需要注意:

  1. @PartMap 注解仅能在被 @Multipart 注解标注的方法中使用,否则抛出异常,
  2. 被标注的参数类型必须是 Map,否则抛出异常,
  3. Map 中键的类型必须是 String 类型,否则抛出异常,如果键的值为 null,则抛出异常,
  4. Map 中值的类型不能是 okhttp3.MultipartBody.Part ,否则抛出异常,
  5. Map 中值如果值为 null ,则抛出异常,
  6. Map 中值会经过 Retrofit#requestBodyConverter(Type, Annotation[], Annotation[]) 转换,如果转换后的值为 null,则抛出异常,

总结

@Part@PartMap 注解与 @Field@FieldMap 注解在类型和适用场景方面非常类似:

注解类型适用场景
@Part动态明确 name 参数值,不明确 Part 内容
@PartMap动态1.name 参数值和 Part 内容都不明确
2.一次传入多个 Part

但是,在功能支持方面,@Part@PartMap 注解相对于 @Field@FieldMap 注解要强大一些:前者支持文件上传,由于文件上传较为复杂,操作步骤较多,那么前者的易用性自然就有所降低。

总之,@Part@PartMap 注解在使用时还是需要多多注意的。

希望可以帮你更好的使用 Retrofit,happy~