You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异常发生时如何获取API的URL端点与GET/POST请求方法?

如何在API异常时获取请求方法和URL端点

当前你的safeCallApi函数仅接收一个API调用的lambda,无法直接从这个lambda中提取请求信息。要解决这个问题,可以从主动传入请求信息和从异常/响应中提取两个方向入手,以下是具体方案:

方案一:主动传入请求方法和URL

直接将请求的method和url作为参数传入safeCallApi,这样无论请求是否成功(包括未发送就抛出异常的情况),都能拿到对应的请求信息:

// 先给自定义异常类添加method和url字段
open class ApiException(val method: String, val url: String, message: String? = null) : Exception(message)
class BodyNullException(method: String, url: String) : ApiException(method, url, "Response body is null")
class ServerException(method: String, url: String) : ApiException(method, url, "Server response failed")
class UnknownException(cause: Throwable, method: String, url: String) : ApiException(method, url, cause.message, cause)

// 修改safeCallApi函数
suspend fun <T : BaseResponse> safeCallApi(
    method: String,
    url: String,
    api: suspend () -> Response<T>
): Result<T> {
    return try {
        val response = api()
        // 从Response中获取实际的请求信息(避免传入的参数和实际请求不一致)
        val actualMethod = response.raw().request.method
        val actualUrl = response.raw().request.url.toString()
        
        if (response.isSuccessful) {
            response.body()?.let { Result.success(it) } 
                ?: Result.failure(BodyNullException(actualMethod, actualUrl))
        } else {
            Result.failure(ServerException(actualMethod, actualUrl))
        }
    } catch (e: Exception) {
        // 异常时使用传入的请求信息(因为此时可能还未拿到Response)
        Result.failure(UnknownException(e, method, url))
    }
}

// 调用示例
safeCallApi("GET", "/api/user/1") { apiService.getUser(1) }

方案二:从Retrofit异常/响应中提取

如果使用Retrofit作为网络框架,可以利用它的HttpException类获取响应中的请求信息;对于非HTTP异常(如网络断开),还是需要主动传入或通过拦截器提前注入:

步骤1:从HttpException中提取信息

当请求已经发送但返回非2xx状态码时,异常会被包装成HttpException,可以从中拿到原始请求信息:

catch (e: Exception) {
    var method = ""
    var url = ""
    
    if (e is HttpException) {
        val rawRequest = e.response()?.raw()?.request
        method = rawRequest?.method ?: ""
        url = rawRequest?.url.toString() ?: ""
    }
    
    // 这里如果是非HttpException,method和url需要提前传入,或者通过其他方式获取
    Result.failure(UnknownException(e, method, url))
}

步骤2:用拦截器给异常注入请求信息

如果不想每次调用都传入method和url,可以通过Retrofit的拦截器,将请求信息附加到异常的上下文或自定义异常中:

class RequestInfoInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        return try {
            chain.proceed(request)
        } catch (e: IOException) {
            // 将请求信息包装到自定义异常中抛出
            throw RequestIOException(request.method, request.url.toString(), e)
        }
    }
}

class RequestIOException(val method: String, val url: String, cause: IOException) : IOException(cause.message, cause)

// 添加拦截器到Retrofit
val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addInterceptor(RequestInfoInterceptor())
    // ...其他配置
    .build()

// 然后在safeCallApi中捕获
catch (e: Exception) {
    val (method, url) = when(e) {
        is HttpException -> {
            val req = e.response()?.raw()?.request
            req?.method ?: "" to req?.url.toString() ?: ""
        }
        is RequestIOException -> e.method to e.url
        else -> "" to ""
    }
    Result.failure(UnknownException(e, method, url))
}

总结

  • 如果需要覆盖所有异常场景(包括未发送请求的情况),主动传入请求信息是最可靠的方式;
  • 如果使用Retrofit,结合HttpException和自定义拦截器,可以减少重复传参的工作量。

内容的提问来源于stack exchange,提问作者vstung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 04:52:20