2. RESTful 接口定义规范与注解体系:@GET / @POST / @PUT / @DELETE、@Path、@Query、@Body
RESTful 风格是现代 API 设计的主流范式,Retrofit 通过注解将接口声明与 HTTP 请求无缝映射。本章围绕核心注解展开,解释每个注解的含义、适用场景以及如何在 Retrofit 中正确使用。
2.1 HTTP 方法与 Retrofit 注解对照
RESTful 规范使用 HTTP 动词表达资源操作:GET 查询、POST 创建、PUT 全量更新、DELETE 删除。Retrofit 提供同名注解,可附加 URL 路径。
| HTTP 方法 | Retrofit 注解 | 典型语义 | 常用配合注解 |
|---|---|---|---|
| GET | @GET | 获取资源 | @Path、@Query、@QueryMap |
| POST | @POST | 创建新资源 | @Body、@Field、@Part |
| PUT | @PUT | 全量更新资源 | @Body、@Path |
| DELETE | @DELETE | 删除资源 | @Path、@Query |
规范提示: RESTful 要求 URL 使用名词复数形式,避免动词;如
/users/{id} 而非 /getUser。Retrofit 注解的 value 参数应与后端设计完全一致。
2.2 @Path:动态路径片段
将方法参数动态注入 URL 路径中,用 {参数名} 占位。适用于资源 ID、日期等可变部分。
public interface UserService {
@GET("users/{id}")
Call<User> getUserById(@Path("id") long userId);
}
运行时调用 getUserById(42) 会发起 GET /users/42 请求。
2.3 @Query:URL 查询参数
拼接在 ? 后的键值对,用于过滤、分页等。Retrofit 自动处理编码。
public interface UserService {
@GET("users")
Call<List<User>> getUsers(
@Query("page") int page,
@Query("size") int size,
@Query("name") String name // 可为 null,此时不拼接该参数
);
}
调用 getUsers(1, 20, "Alice") 生成 GET /users?page=1&size=20&name=Alice。
2.4 @Body:请求体序列化
将对象自动序列化为 JSON(配合自定义 Converter)放入请求体。用于 POST/PUT 提交数据。
public interface UserService {
@POST("users")
Call<User> createUser(@Body User user);
}
Retrofit 通过注册的 GsonConverterFactory 或 MoshiConverterFactory 完成序列化。请求头中自动添加 Content-Type: application/json。
2.5 注解组合与参数位置
路径、查询、Body 可混合使用,但每个方法只能有一个 @Body。
@PUT("users/{id}")
Call<User> updateUser(
@Path("id") long id,
@Body User updatedUser
);
2.6 注解与接口定义映射流程
下图展示从 RESTful 资源定义到 Retrofit 注解声明的完整映射关系:
2.7 常见错误与调试
- 路径占位符不匹配:
@Path("id")但 URL 中用{userId}会导致IllegalArgumentException。务必保持 name 一致。 - @Body 与 @Field 混用:两者不能共存于同一方法,
@Field用于表单(@FormUrlEncoded),@Body用于 JSON。 - @Query 参数未编码:Retrofit 默认使用
encoded = false自动编码。若提前编码需@Query(encoded = true)。 - 注解 value 中路径末尾斜杠:建议统一以
/结尾或都不要,避免拼接后出现双斜杠//。
性能注意: 大量
@QueryMap 参数会生成冗长 URL,影响网络传输效率。建议将静态参数放入注解 value 中,仅动态参数使用 @Query。另外,@Body 序列化对象应避免过度嵌套,减少 JSON 体积。
2.8 调试最佳实践
在 OkHttp 日志拦截器中打印最终请求 URL 和 Body,确认注解拼接结果:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
日志中可见 --> GET /users?page=1&size=20&name=Alice 等完整请求行,便于定位注解问题。