现在有需求写了个 Kotlin 的:
主要分为以下几个类:
HttpManager:okhttp3 创建过程的封装
RedirectInterceptor:拦截器,主要针对 307 跳转的重新请求,也可以添加其他的拦截器
RequstManager:对外提供使用的工具类
ReponseHandler:请求返回的接口层,具体实现由使用方决定
-
- import okhttp3.Interceptor
- import okhttp3.OkHttpClient
- import java.util.concurrent.TimeUnit
-
- class HttpManager {
-
- var httpClient: OkHttpClient? = null
- val timeUnit: TimeUnit = TimeUnit.SECONDS
- val connectTimeOut: Long = 10
- val readTimeOut: Long = 10
- val writeTimeOut: Long = 10
- var interceptors: List<Interceptor> = listOf(RedirectInterceptor())
- var networkInterceptors: List<Interceptor> = ArrayList()
-
- companion object {
- fun instance() = Holder.INSTANCE
- }
-
- private object Holder {
- val INSTANCE = HttpManager()
-
- init {
- INSTANCE.initHttpClient()
- }
- }
-
- fun initHttpClient(): OkHttpClient? {
- var builder = OkHttpClient.Builder()
- .connectTimeout(connectTimeOut, timeUnit)
- .readTimeout(readTimeOut, timeUnit)
- .writeTimeout(writeTimeOut, timeUnit)
- builder = initInterceptor(builder, interceptors)
- builder = initNetworkInterceptor(builder, networkInterceptors)
- httpClient = builder.build()
- return httpClient
- }
-
- fun initInterceptor(builder: OkHttpClient.Builder?, interceptors: List<Interceptor>?): OkHttpClient.Builder? {
- interceptors?.forEach { i: Interceptor? ->
- if (i != null)
- builder?.addInterceptor(i)
- }
- return builder
- }
-
- fun initNetworkInterceptor(builder: OkHttpClient.Builder?, networkInterceptors: List<Interceptor>?): OkHttpClient.Builder? {
- networkInterceptors?.forEach { i: Interceptor? ->
- if (i != null)
- builder?.addNetworkInterceptor(i)
- }
- return builder
- }
- }
- import okhttp3.Interceptor
- import okhttp3.Request
- import okhttp3.Response
- import org.slf4j.Logger
- import org.slf4j.LoggerFactory
- import javax.servlet.http.HttpServletResponse.SC_TEMPORARY_REDIRECT
-
- class RedirectInterceptor : Interceptor {
- val logger: Logger = LoggerFactory.getLogger(RedirectInterceptor::class.java)
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- var response = chain.proceed(request)
- val code = response.code()
- if (code == SC_TEMPORARY_REDIRECT) {
- //获取重定向的地址
- val location = response.headers()["Location"]
- logger.info("重定向地址:", "location = $location")
- //重新构建请求
- val newRequest: Request = request.newBuilder().url(location).build()
- response = chain.proceed(newRequest)
- }
- return response
- }
- }
- import com.google.common.base.Strings
- import okhttp3.FormBody
- import okhttp3.Interceptor
- import okhttp3.MediaType
- import okhttp3.OkHttpClient
- import okhttp3.Request
- import okhttp3.RequestBody
-
- class RequestManager {
-
- var okHttpClient: OkHttpClient? = null
- var builder: Request.Builder? = null
-
- init {
- builder = Request.Builder()
- }
-
- constructor() {
- okHttpClient = HttpManager.instance().httpClient
- }
-
- constructor(okHttpClient: OkHttpClient?) {
- if (okHttpClient == null)
- RequestManager()
- else
- this.okHttpClient = okHttpClient
- }
-
- fun doGet(url: String, headers: MutableMap<String, String>? = null, params: MutableMap<String, String>? = null): RequestManager {
- if (url.isBlank())
- return this
- if (headers != null)
- addHeaders(headers)
- builder!!.url(if (params != null) setGetParams(url, params) else url).get()
- return this
- }
-
- fun doPost(url: String, headers: MutableMap<String, String>? = null, params: MutableMap<String, String>? = null): RequestManager {
- if (url.isBlank())
- return this
- if (headers != null)
- addHeaders(headers)
- builder!!.url(url)
- builder!!.post(setPostParams(params))
- return this
- }
-
- fun doPut(url: String, headers: HashMap<String, String>? = null, content: String?): RequestManager {
- if (url.isBlank())
- return this
- if (headers != null)
- addHeaders(headers)
- builder!!.url(url)
- var requestBody = setPutBody(content)
- if (requestBody != null) {
- builder!!.put(requestBody)
- }
- return this
- }
-
- fun addHeaders(headers: MutableMap<String, String>) {
- headers.entries.forEach { entry ->
- headers.keys
- builder?.addHeader(entry.key, entry.value)
- }
- }
-
- fun setGetParams(url: String, params: MutableMap<String, String>): String {
- var sb = StringBuilder(url)
- if (params.isNotEmpty()) sb.append("?") else sb
- params.forEach { entry ->
- params.keys
- sb.append(entry.key + "=" + entry.value + "&")
- }
- return if (sb.toString().endsWith("&")) sb.subSequence(0, sb.lastIndex).toString() else sb.toString()
- }
-
- fun setPostParams(params: MutableMap<String, String>?): RequestBody? {
- var builder = FormBody.Builder()
- params?.forEach { entry ->
- params.keys
- builder.add(entry.key, entry.value)
- }
- return builder.build()
- }
-
- fun setPutBody(content: String?): RequestBody? {
- if (Strings.isNullOrEmpty(content)) {
- return null
- }
- return RequestBody.create(MediaType.parse("application/json"), content)
- }
-
- fun <T> execute(responseHandler: ResponseHandler<T>): T? {
- val response = okHttpClient!!.newCall(builder!!.build()).execute()
- return responseHandler.apply(response)
- }
- }
- import okhttp3.Response
-
- interface ResponseHandler<T> {
- fun apply(response: Response): T
- }
以下是使用方的代码,返回结果是 Result<List<T>> 和 Result<T> 的格式,所以针对两种返回,写了两个不同的 JavaType, 用于 Jackson 的反序列化使用,避免因为泛型导致反序列化嵌套 json 数据的内层数据为 LinkedHaspMap
- fun <T> fromClass(inner: Class<T>): BaseResponseHandler<T> {
- return BaseResponseHandler(JsonUtil.getObjectMapper().typeFactory.constructType(inner))
- }
-
- fun <T> fromListClass(inner: Class<T>): BaseResponseHandler<List<T>> {
- return BaseResponseHandler(JsonUtil.getObjectMapper().typeFactory.constructParametricType(List::class.java, inner))
- }
-
- class BaseResponseHandler<T>(javaType: JavaType) : ResponseHandler<Result<T>> {
- private var javaType: JavaType
-
- init {
- this.javaType = JsonUtil.getObjectMapper().typeFactory.constructParametricType(Result::class.java, javaType)
- }
-
- override fun apply(response: okhttp3.Response): Result<T> {
- return JsonUtil.getObjectMapper().readValue(response.body()!!.string(), javaType)
- }
-
- }
使用方调用:
var imageVersion = RequestManager().doGet(host + vhost.common + instance.getVersionByIp, null, mutableMapOf(Pair("ip", heartbeatInfo.agentIp))).execute(fromClass(String::class.java))?.data
var idleInstanceFromVhost = RequestManager().doGet(host + vhost.common + instance.getIdleInstanceByImageVersion, null, mutableMapOf(Pair("projectEnv", "ue-" + env), Pair("imageVersion", imageVersion ?: ""))).execute(fromListClass(InstanceListResp::class.java))?.data