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

从OkHttpClient+Gson转Volley+JSONObject遇307错误求助

从OkHttpClient+Gson迁移到Volley+JSONObject时的307重定向错误解决

问题描述

我是API集成领域的新手,原本用OkHttpClient+Gson实现的音频评分服务请求功能运行正常,但由于应用另一部分依赖Volley库和JSONObject,遂进行迁移。修改后遇到错误:

E [93] NetworkUtility.shouldRetryException: Unexpected response code 307 for https://url/score

尝试处理重定向后,要么出现类似错误,要么请求无限循环,恳请帮助理解错误含义并提供解决办法。

原有OkHttpClient+Gson实现代码

private suspend fun scoreAudioWithASR(audioUri: Uri, text: String): String {

    val audioBuffer = uriToBase64(context, audioUri)

    val requestDict = mapOf(
        "lang_code" to "voice",
        "gender" to "M",
        "text" to text,
        "audio" to audioBuffer,
        "tag" to "test"
    )

//        inspect the data produced for debugging purposes
//        saveRequestData(requestDict)

    val gson = Gson()

    val requestBody = gson.toJson(requestDict)
        .toRequestBody("json; charset=utf-8".toMediaTypeOrNull())

    val requestBodyJsonString = gson.toJson(requestDict)
    Log.d("JSON Object", requestBodyJsonString)

    val request = Request.Builder()
        .url("https://url/score")
        .post(requestBody)
        .build()

    val client = OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .writeTimeout(60, TimeUnit.SECONDS)
        .build()

    return withContext(Dispatchers.IO) {
        try {
            client.newCall(request).execute().use { response ->
                if (!response.isSuccessful) throw IOException("Unexpected code $response")
                val responseDict = gson.fromJson(response.body?.string(), Map::class.java)
                val utteranceDp = responseDict["utterance_dp"] as Double

                Log.d("utteranceDp",utteranceDp.toString() )

                val utteranceThresholds = responseDict["utterance_thresholds"] as List<Double>

                Log.d("utteranceThresholds",utteranceThresholds.toString() )

                if (utteranceDp > utteranceThresholds[1]) "good" else "try again"
            }
        } catch (e: Exception) {
            Log.e("ASRScore", "Error making ASR request", e)
            "error message."
        }
    }
}

迁移后的Volley代码

private suspend fun scoreAudioWithASR(
audioUri: Uri,
text: String,
): String {
return suspendCoroutine { continuation ->
val audioBuffer = uriToBase64(context, audioUri)
val url = "https://url/score/"

    val jsonObject = JSONObject().apply {
        put("lang_code", "zul_kids")
        put("gender", "M")
        put("text", text)
        put("audio", audioBuffer)
        put("tag", "test")
    }

// Convert JSON object to a formatted string
val jsonString = jsonObject.toString(4)

// Log the formatted JSON string
Log.d("JSON Object", jsonString)

    val queue = Volley.newRequestQueue(context)

    val jsonObjectRequest = JsonObjectRequest(
        Request.Method.GET, url, jsonObject,
        { response ->
            // Handle successful response
            val responseDict = response as Map<*, *>
            val utteranceDp = responseDict["utterance_dp"] as Double
            val utteranceThresholds = responseDict["utterance_thresholds"] as List<Double>

            val result = if (utteranceDp > utteranceThresholds[1]) {
                "WWell done!"
            } else {
                "Not good, try again"
            }
            continuation.resume(result)
        },
        { error ->
            // Handle Volley error
            Log.e("API Error", "Error fetching game configuration: ${error.message}")
            continuation.resumeWithException(error)
        }
    )
    queue.add(jsonObjectRequest)
}
}

错误原因分析

  • 307临时重定向:该状态码表示请求需要临时转发到新的URL,且必须保持原请求的方法和请求体不变。OkHttpClient默认会自动处理这类重定向,但Volley的默认实现存在缺陷。
  • 请求方法不匹配:原有代码使用POST方法发送请求体,但迁移后的代码误用了GET方法。GET请求携带JSON体不符合HTTP规范,服务器返回307后Volley无法正确处理请求体的重发,导致错误或循环。
  • URL不一致:原有URL是https://url/score,迁移后的URL多了末尾斜杠https://url/score/,服务器可能因此返回重定向,进一步触发Volley的处理问题。
  • 参数不一致:原有代码的lang_code是voice,迁移后改成了zul_kids,可能导致服务器返回非预期响应。

解决办法及修正后的代码

关键修正点

  1. 将请求方法改回POST,与原有代码保持一致
  2. 统一URL,去掉末尾多余的斜杠
  3. 修正lang_code参数,保持和原有代码一致
  4. 确保请求头设置正确的Content-Type为application/json

修正后的Volley代码

private suspend fun scoreAudioWithASR(audioUri: Uri, text: String): String {
    return suspendCoroutine { continuation ->
        val audioBuffer = uriToBase64(context, audioUri)
        val url = "https://url/score" // 统一URL,去掉末尾斜杠

        val jsonObject = JSONObject().apply {
            put("lang_code", "voice") // 修正参数,和原有代码一致
            put("gender", "M")
            put("text", text)
            put("audio", audioBuffer)
            put("tag", "test")
        }

        val jsonString = jsonObject.toString(4)
        Log.d("JSON Object", jsonString)

        val queue = Volley.newRequestQueue(context)

        // 使用POST方法,正确发送JSON请求体
        val jsonObjectRequest = object : JsonObjectRequest(
            Request.Method.POST, url, jsonObject,
            { response ->
                try {
                    val utteranceDp = response.getDouble("utterance_dp")
                    val utteranceThresholds = response.getJSONArray("utterance_thresholds")
                    // 将JSONArray转换为List<Double>
                    val thresholdsList = mutableListOf<Double>()
                    for (i in 0 until utteranceThresholds.length()) {
                        thresholdsList.add(utteranceThresholds.getDouble(i))
                    }

                    val result = if (utteranceDp > thresholdsList[1]) {
                        "Well done!"
                    } else {
                        "Not good, try again"
                    }
                    continuation.resume(result)
                } catch (e: JSONException) {
                    Log.e("API Parse Error", "Failed to parse response", e)
                    continuation.resumeWithException(e)
                }
            },
            { error ->
                Log.e("API Error", "Error making ASR request: ${error.message}", error)
                continuation.resumeWithException(error)
            }
        ) {
            // 确保请求头设置正确的Content-Type
            override fun getHeaders(): MutableMap<String, String> {
                val headers = HashMap<String, String>()
                headers["Content-Type"] = "application/json; charset=utf-8"
                return headers
            }
        }

        // 设置超时时间,和原有代码保持一致
        jsonObjectRequest.setRetryPolicy(DefaultRetryPolicy(
            60000, // 60秒超时
            DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
        ))

        queue.add(jsonObjectRequest)
    }
}

额外建议

如果服务器仍返回307且Volley无法处理,可自定义Volley的HurlStack来处理重定向:

val stack = object : HurlStack() {
    override fun performRequest(request: Request<*>?, additionalHeaders: MutableMap<String, String>?): HttpResponse {
        val httpResponse = super.performRequest(request, additionalHeaders)
        val statusCode = httpResponse.statusCode
        if (statusCode == 307) {
            val location = httpResponse.headers.get("Location")
            if (!location.isNullOrEmpty()) {
                // 重新构造请求到新URL,保持方法和请求体
                val newRequest = Request.Builder()
                    .url(location)
                    .method(request?.method, request?.body)
                    .headers(request?.headers)
                    .build()
                return super.performRequest(newRequest, additionalHeaders)
            }
        }
        return httpResponse
    }
}
val queue = Volley.newRequestQueue(context, stack)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 23:58:13