Retrofit2身份验证错误到IBM的语音转文本 [英] Retrofit2 authentication error to IBM's Speech to Text

查看:80
本文介绍了Retrofit2身份验证错误到IBM的语音转文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试不使用库而访问IBM的语音转文本服务.我正在GSON上使用Retrofit.

I am trying to access IBM's Speech to Text service without using the library. I am using Retrofit with GSON.

问题出在身份验证中,显然不正确,返回了代码401.从

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

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

当我使用凭据测试curl命令时,该服务运行正常.

When I test the curl command with my credentials, the service works fine.

这是我正在使用的界面

interface SpeechToTextApi {

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

我具有以下数据类

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

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()
    }
})

录音是MP3文件,对于这些文件,我相信它们可以正确存储并可以访问.我也用audio/mp3替换了audio/flac.

Recordings are MP3 files, for which I am sure they are stored correctly and accessible. I have replaced audio/flac with audio/mp3 as well.

问题似乎是验证工作的方式.在上面显示的代码之前,我已经使用

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()

,但相同的响应代码401仍然存在.当然,接口方法缺少@Header参数.

but the same response code 401 persisted. Of course, the interface method lacked the @Header parameter.

非常感谢您提供任何帮助.

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.

curl命令可以看到,身份验证采用username: password模式的形式,在这种情况下,用户名是apikey字符串,密码是您的API密钥.

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.

因此,您应该通过以下方式构建Retrofit实例来解决此问题:

So the way you should tackle this is by building your Retrofit instance this way:

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()
}

并创建此自定义拦截器

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())
    }
}

您需要使用Credentials.basic()来对凭据进行编码.

You need to use Credentials.basic() in order to encode credentials.

我真的希望遇到类似问题的人偶然发现这一点并节省一些时间.

I really hope somebody with a similar issue stumbles across this and saves themselves some time.

这篇关于Retrofit2身份验证错误到IBM的语音转文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆