异常发生时如何获取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
相关产品推荐
相关产品推荐

