使用自定义注解+拦截器为Retrofit接口添加固定值参数

需求分析

当我们按照Retrofit 的官方示例来创建API接口时,我们可以在接口定义上非常清晰地看到:请求地址,请求方法,它有哪些参数,各个参数是以何种方式传递的。所以在做扩展功能时我们希望延续这种一目了然,因此具体需求为:通过在已有的Retrofit API接口方法上,以增加自定义注解的方式来为请求增加固定值的参数

参考来源

使用最简单的方式扩展 retrofit 的注解类型

核心原理

Retrofit 在构建OkHttp.Request时,将被调用的方法使用tag方法保存在了构建好的Request实例中,因此只需要使用拦截器从Request实例中获取到该方法即可读取上面的注解,并添加参数

参考代码

自定义注解,这里定义了两个注解来适应需要单个参数和多个参数的情况

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class StaticQuery(
    val key: String,
    val value: String,
)
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class StaticQueries(
    val value: Array<StaticQuery>,
)

拦截器,从Request实例中获取方法并遍历方法上的注解

class StaticQueryInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        // 获取retrofit创建Request时在tag中保存的被调用的方法, 如果没获取到则直接返回
        val method = request.tag(Invocation::class.java)?.method() ?: return chain.proceed(request)
        val urlBuilder = request.url.newBuilder()
        // 遍历方法上的注解, 处理其中的自定义注解
        for (annotation in method.annotations) {
            if (annotation is StaticQuery) {
                urlBuilder.addQueryParameter(annotation.key, annotation.value)
            }
            if (annotation is StaticQueries) {
                for (staticParam in annotation.value) {
                    urlBuilder.addQueryParameter(staticParam.key, staticParam.value)
                }
            }
        }
        val newRequest = request.newBuilder().url(urlBuilder.build()).build()
        return chain.proceed(newRequest)

    }
}

表单参数的情况类似,注解:

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class StaticField(
    val key: String,
    val value: String,
)
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class StaticFields(
    val value: Array<StaticField>,
)

拦截器

class StaticFieldInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        // 获取retrofit创建Request时在tag中保存的被调用的方法, 如果没获取到则直接返回
        val method = request.tag(Invocation::class.java)?.method() ?: return chain.proceed(request)
        // 获取请求体,如果不存在则直接返回
        val requestBody = request.body ?: return chain.proceed(request)

        val newRequestBuilder = request.newBuilder()

        if (requestBody is FormBody) {
            // 复制原表单数据
            val formBuilder = FormBody.Builder()
            for (i in 0 until requestBody.size) {
                formBuilder.addEncoded(requestBody.encodedName(i), requestBody.encodedValue(i))
            }
            // 添加注解上的静态参数
            val annotations = findAnnotations(method.annotations)
            for (staticField in annotations) {
                formBuilder.add(staticField.key, staticField.value)
            }
            newRequestBuilder.post(formBuilder.build())
        }
        return chain.proceed(newRequestBuilder.build())
    }
    companion object {
        @JvmStatic
        fun findAnnotations(annotations: Array<Annotation>): List<StaticField> {
            val list = ArrayList<StaticField>()

            for (annotation in annotations) {
                if (annotation is StaticField) {
                    list.add(annotation)
                }
                if (annotation is StaticFields) {
                    list.addAll(annotation.value.toList())
                }
            }
            return list
        }
    }
}

使用示例

@FormUrlEncoded
@POST("ajax/novels/bookmarks/delete")
@StaticField(key = "del", value = "1")
fun postNovelDel(@Field("book_id") bookmarkId: Long): Call<PixivResponse<Void>>
@GET("ranking.php")
@StaticQuery(key = "format", value = "json")
fun getIllustrationRanking(
    @Query("p") page: Int? = 1,    
    @Query("mode") mode: RankingMode? = null,
    @Query("content") content: RankingContent? = null,
    @Query("date") date: String? = null,
): Call<IllustrationRankingBody>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值