自定义改造请求

问题描述 投票:0回答:1

我正在尝试使用 Retrofit2 为我的 Android 应用程序向具有以下特征的端点发出 POST 请求:

认证

在执行任何操作之前是必需的,因为它返回用于进一步请求的必要密钥和令牌。

  • 端点:
    https://api.example.com/v1
  • 类型:
    POST
  • 参数:
    • level
      :固定值,始终设置为
      1
  • 身体领域:
    • user
      :要验证的用户名。
    • pass
      :关联密码。
    • new_pass
      (可选):登录成功后将账户密码更改为此值。
    • api_key
      : API 密钥。

此端点用于对用户进行身份验证(之后可以选择更改其密码),但它需要存在 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
来实现这个结果吗?

举个例子,我们将不胜感激。

android retrofit2
1个回答
0
投票

我相信您可以通过多种方式实现这一目标。

  1. 通过使用泛型

    数据类 ApiRequest( @Field("api_key") val apiKey: String = API_KEY, 验证数据:T )

    数据类LoginRequest( @Field("user") val 用户名:字符串, @Field("pass") val 密码:字符串, @Field("new_pass") val newPassword: 字符串? = 空 )

  2. 使用拦截添加应该始终存在的代码。

    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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.