我正在尝试使用 Retrofit2 为我的 Android 应用程序向具有以下特征的端点发出 POST 请求:
认证
在执行任何操作之前是必需的,因为它返回用于进一步请求的必要密钥和令牌。
- 端点:
。https://api.example.com/v1
- 类型:
。POST
- 参数:
:固定值,始终设置为level
。1
- 身体领域:
:要验证的用户名。user
:关联密码。pass
(可选):登录成功后将账户密码更改为此值。new_pass
: API 密钥。api_key
此端点用于对用户进行身份验证(之后可以选择更改其密码),但它需要存在 API 密钥 - 该密钥是在应用程序中烘焙的,不是用户定义的。
基本上,登录请求将如下所示:
curl --request POST 'https://api.example.com/v1?level=1' \
--form 'user="my_username"' \
--form 'pass="my_password"' \
# --form 'new_pass="my_new_password"' \
--form 'api_key="abc"'
现在,Retrofit 请求可以在定义时进行自定义,但到达这一点后我陷入了困境,因为我无法按照我想要的方式配置请求(强制、可选和固定值有效负载):
private const val API_KEY = "abc"
interface ExampleApiService {
@FormUrlEncoded
@POST("?level=1")
fun login(
@Field("user") username: String,
@Field("pass") password: String,
)
}
据我了解,在定义登录函数时,我可以传递一个
@FieldMap params: Map<String, String>
作为参数,并在需要时简单地添加我需要的内容:
interface ExampleApiService {
@FormUrlEncoded
@POST("?level=1")
fun login(@FieldMap params: Map<String, String>)
}
但这意味着将其保留在调用代码中以传递必要的参数,我发现这非常不优雅。
我知道我可以重载
login
函数来处理多种情况,或者我可以有多个“登录类型”函数,但如果有多个可选参数,它们会很快加起来。
另一种选择是包装我的代码并让该包装器进行正确的计算,但我想避免另一个中间人。
我想要实现的目标是沿着这些思路 - 我想使用注释来定义 API:
interface ExampleApiService {
@FormUrlEncoded
@POST("?level=1")
@Field("api_key", API_KEY)
fun login(
@Field("user") username: String,
@Field("pass") password: String,
@Field("new_pass") newPassword: String? = null,
)
}
这可以通过改造来实现吗?我可以使用
RequestBody
来实现这个结果吗?
举个例子,我们将不胜感激。
我相信您可以通过多种方式实现这一目标。
通过使用泛型
数据类 ApiRequest( @Field("api_key") val apiKey: String = API_KEY, 验证数据:T )
数据类LoginRequest( @Field("user") val 用户名:字符串, @Field("pass") val 密码:字符串, @Field("new_pass") val newPassword: 字符串? = 空 )
使用拦截添加应该始终存在的代码。
ApiKeyInterceptor 类:拦截器 { 覆盖有趣的拦截(链:Interceptor.Chain):响应{ val 原始请求 = chain.request()
// Create a new FormBody.Builder
val formBodyBuilder = FormBody.Builder()
// Add api_key as a field
formBodyBuilder.add("api_key", API_KEY)
val originalBody = originalRequest.body()
if (originalBody is FormBody) {
for (i in 0 until originalBody.size()) {
formBodyBuilder.add(originalBody.name(i), originalBody.value(i))
}
}
// Build the new FormBody
val newFormBody = formBodyBuilder.build()
val modifiedRequest = originalRequest.newBuilder()
.method(originalRequest.method(), newFormBody)
.build()
return chain.proceed(modifiedRequest)
}
}
但是,我建议将 API 密钥添加为标头的一部分,这样拦截就会像
class ApiKeyInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val modifiedRequest = originalRequest.newBuilder()
.addHeader("api_key", API_KEY)
.build()
return chain.proceed(modifiedRequest)
}
}