简体   繁体   English

IBM 的 Speech to Text 的 Retrofit2 身份验证错误

[英]Retrofit2 authentication error to IBM's Speech to Text

I am trying to access IBM's Speech to Text service without using the library.我试图在不使用库的情况下访问 IBM 的 Speech to Text 服务。 I am using Retrofit with GSON.我正在使用 Retrofit 和 GSON。

The issue is in the authentication, which apparently does not occur correctly, returning code 401. From the official documentation , the HTTP request should come in this format问题出在身份验证中,显然没有正确发生,返回代码 401。从官方文档中,HTTP 请求应该采用这种格式

curl -X POST -u "apikey:{apikey}" \
--header "Content-Type: audio/flac" \
--data-binary @{path_to_file}audio-file.flac \
"{url}/v1/recognize"

When I test the curl command with my credentials, the service works fine.当我使用我的凭据测试curl命令时,该服务运行良好。

This is the interface I'm using这是我正在使用的界面

interface SpeechToTextApi {

    @Multipart
    @POST("v1/recognize")
    fun speechToText(
        @Header("Authorization") authKey: String,
        @Part("file") filename: RequestBody,
        @Part voiceFile: MultipartBody.Part
    ): Call<List<SpeechToText>>
}

where I have the following data classes我有以下数据类

data class SpeechToText(val results: List<SttResult>)
data class SttResult(val alternatives: List<RecognitionResult>, val final: Boolean)
data class RecognitionResult(val confidence: Float, val transcript: String)

and this is how I set up Retrofit这就是我设置 Retrofit 的方式

private val retrofit = Retrofit.Builder()
        .baseUrl(STT_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

private val service = retrofit.create(SpeechToTextApi::class.java)

while calling the actual service looks like this调用实际服务时看起来像这样

val requestFile = RequestBody.create(MediaType.parse("audio/mp3"), file.name)
val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
service
    .speechToText(getString(R.string.stt_iam_api_key), requestFile, body)
    .enqueue(object: Callback<List<SpeechToText>> {
    override fun onResponse(call: Call<List<SpeechToText>>, response: Response<List<SpeechToText>>) {
        val listOfStts = response.body()
        Log.d(TAG, "Response code: ${response.code()}")
        if (listOfStts != null) {
            for (stt in listOfStts) {
                for (res in stt.results) {
                    Log.d(TAG, "Final value: ${res.final}")
                    for (alt in res.alternatives) {
                        Log.d(TAG, "Alternative confidence: ${alt.confidence}\nTranscript: ${alt.transcript}")
                        Toast.makeText(this@MainActivity, alt.transcript, Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }
    }

    override fun onFailure(call: Call<List<SpeechToText>>, t: Throwable) {
        Log.d(TAG, "Error: ${t.message}")
        t.printStackTrace()
    }
})

Recordings are MP3 files, for which I am sure they are stored correctly and accessible.录音是 MP3 文件,我确信它们存储正确且可访问。 I have replaced audio/flac with audio/mp3 as well.我也用audio/mp3替换了audio/flac

Issue seems to be in the way authentication works.问题似乎在于身份验证的工作方式。 Prior to the code I have shown above, I've used在我上面显示的代码之前,我使用过

private val retrofit = Retrofit.Builder()
        .baseUrl(STT_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(OkHttpClient.Builder()
            .addInterceptor { chain ->
                val request = chain.request()
                val headers = request
                    .headers()
                    .newBuilder()
                    .add("Authorization", getString(R.string.stt_iam_api_key))
                    .build()
                val finalRequest = request.newBuilder().headers(headers).build()
                chain.proceed(finalRequest)
            }
            .build())
    .build()

but the same response code 401 persisted.但相同的响应代码 401 仍然存在。 Of course, the interface method lacked the @Header parameter.当然,接口方法缺少@Header参数。

Any sort of help is much appreciated.非常感谢任何形式的帮助。

I am kind of saddened by the fact nobody was able to solve this one sooner, but here's the solution I came across by accident when working on a different project altogether.我对没有人能够更早解决这个问题感到有点难过,但这是我在完全不同的项目上工作时偶然遇到的解决方案。

As you can see from the curl command, authentication comes in the form of username: password pattern, in this case, username being apikey string and password is your API key.curl命令可以看出,身份验证采用username: password模式的形式,在这种情况下,用户名是apikey字符串,密码是您的 API 密钥。

So the way you should tackle this is by building your Retrofit instance this way:所以你应该解决这个问题的方法是这样构建你的 Retrofit 实例:

fun init(token: String) {
    //Set logging interceptor to BODY and redact Authorization header
    interceptor.level = HttpLoggingInterceptor.Level.BODY
    interceptor.redactHeader("Authorization")

    //Build OkHttp client with logging and token interceptors
    val okhttp = OkHttpClient().newBuilder()
        .addInterceptor(interceptor)
        .addInterceptor(TokenInterceptor(token))
        .build()

    //Set field naming policy for Gson
    val gsonBuilder = GsonBuilder()
    gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)

    //Build Retrofit instance
    retrofit = Retrofit.Builder()
        .baseUrl(IBM_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
        .client(okhttp)
        .build()
}

and create this custom interceptor并创建这个自定义拦截器

class TokenInterceptor constructor(private val token: String) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val original = chain.request()
        val requestBuilder = original
            .newBuilder()
            .addHeader("Authorization", Credentials.basic("apikey", token))
            .url(original.url)
        return chain.proceed(requestBuilder.build())
    }
}

You need to use Credentials.basic() in order to encode credentials.您需要使用Credentials.basic()来对凭证进行编码。

I really hope somebody with a similar issue stumbles across this and saves themselves some time.我真的希望有类似问题的人偶然发现这一点并节省一些时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM