Добавить параметр заголовка в модификации

Я пытаюсь позвонить и API, который требует от меня передать ключ API.

Мой Sercive вызов с использованием HtppURLconnection работает отлично.

url = new URL("https://developers.zomato.com/api/v2.1/search?entity_id=3&entity_type=city&q=" + params[0]);
        urlConnection = (HttpURLConnection) url.openConnection();

        urlConnection.setRequestProperty("user-key","9900a9720d31dfd5fdb4352700c");

        if (urlConnection.getResponseCode() != 200) {
            Toast.makeText(con, "url connection response not 200 | " + urlConnection.getResponseCode(), Toast.LENGTH_SHORT).show();
            Log.d("jamian", "url connection response not 200 | " + urlConnection.getResponseCode());
            throw new RuntimeException("Failed : HTTP error code : " + urlConnection.getResponseCode());
        }

Тем не менее, я не уверен, как это работает с RetroFit, поскольку я всегда обращался к Failure. Вот код, который я использую для того же вызова службы

 @GET("search")
Call<String> getRestaurantsBySearch(@Query("entity_id") String entity_id, @Query("entity_type") String entity_type, @Query("q") String query,@Header("Accept") String accept, @Header("user-key") String userkey);

и я использую это, чтобы назвать это

Call<String> call = endpoint.getRestaurantsBySearch("3","city","mumbai","application/json","9900a9720d31dfd5fdb4352700c");

Все эти вызовы идут в метод OnFailure в RetroFit. Если я отправляю его без HeaderParameters, он идет в успех с 403-м, потому что мне, очевидно, нужно где-то передать ключ API, но я не могу понять, как.

@GET("search")
Call<String> getRestaurantsBySearch(@Query("entity_id") String entity_id, @Query("entity_type") String entity_type, @Query("q") String query);

Ошибка, которую я получаю в OnFailure:

java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 2 path $

6 ответов

Решение

Попробовав пару раз, я разобрался с ответом.

Ошибка

java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 2 path $

приходил из-за неудачи разбора JSON.

В вызове метода я передавал String вместо класса POJO.

@Headers("user-key: 9900a9720d31dfd5fdb4352700c")
@GET("api/v2.1/search")
Call<String> getRestaurantsBySearch(@Query("entity_id") String entity_id, @Query("entity_type") String entity_type, @Query("q") String query);

Я должен был передать вместо Call< String> тип Call< Data>

Данные, являющиеся классом Pojo

что-то вроде этого

@Headers("user-key: 9900a9720d31dfd5fdb4352700c")
@GET("api/v2.1/search")
Call<Data> getRestaurantsBySearch(@Query("entity_id") String entity_id, @Query("entity_type") String entity_type, @Query("q") String query);

Попробуйте этот тип заголовка для Retrofit 1.9 и 2.0. Для Json Content Type.

@Headers({"Accept: application/json"})
@POST("user/classes")
Call<playlist> addToPlaylist(@Body PlaylistParm parm);

Вы можете добавить еще много заголовков, т.е.

@Headers({
        "Accept: application/json",
        "User-Agent: Your-App-Name",
        "Cache-Control: max-age=640000"
    })

Вы можете использовать ниже

 @Headers("user-key: 9900a9720d31dfd5fdb4352700c")
 @GET("api/v2.1/search")
 Call<String> getRestaurantsBySearch(@Query("entity_id") String entity_id, @Query("entity_type") String entity_type, @Query("q") String query);

а также

 Call<String> call = endpoint.getRestaurantsBySearch("3","city","cafes");

Вышесказанное основано на API Zomato, который задокументирован в

https://developers.zomato.com/documentation

Следует отметить, что изменение конечной точки api/v2.1/search и заголовка @Headers("user-key: 9900a9720d31dfd5fdb4352700c"),

Также проверьте ваш базовый URL .baseUrl("https://developers.zomato.com/")

Также я попробовал вышеупомянутое с ключом API, который я сгенерировал, и это работает, и мой запрос был кафе, как предложил документация zomato.

Примечание: надеюсь, у вас есть ниже

 .addConverterFactory(ScalarsConverterFactory.create()) // for string conversion
 .build();

и ниже в файле build.gradle

compile group: 'com.squareup.retrofit2', name: 'converter-scalars', version: '2.2.0'

Редактировать:

Вы также можете передать заголовок с динамическим значением, как показано ниже

@GET("api/v2.1/search")
Call<String> getRestaurantsBySearch(@Query("entity_id") String entity_id, @Query("entity_type") String entity_type, @Query("q") String query,@Header("user-key") String userkey);

А также

Call<String> call = endpoint.getRestaurantsBySearch("3","city","cafes","9900a9720d31dfd5fdb4352700c");

Позвольте мне также немного ( на самом деле много ) прокомментировать добавление заголовков в Kotlin с акцентом на внедрение зависимостей .

Лучшим подходом было бы предоставить как OkHttpClient , так и HttpLoggingInterceptor в одном и том же методе di, используя в этом случае удобную функцию Kotlin Scoping.alsoиapply.

Нам понадобятся этиRetrofit (2.9)и зависимости — в этом примере используется Kotlin DSL , но в Groovy он должен быть более или менее таким же. Конечно, вам понадобятся другие зависимости, такие как Hilt, если вы используете Dependency Injection.

      implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.7")
implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.7")

Следующая остановка — создать функцию @Provide , которая возвращает .

      @Provides
@Singleton
fun provideOkHttpClient():OkHttpClient { ...}

Базовая теория о перехватчиках очень важна; чтобы использовать перехватчик, вам нужно создать класс, который реализуетInterceptor interfaceи переопределить метод.

получаетInterceptor.Chainобъект - который представляет текущий запрос и позволяет вам продолжить запрос, вызвавproceed()способом или отменить запрос,throwing an exception. функция переопределения возвращает объект, который является именно тем, что возвращается.

      class MyInterceptor : Interceptor {
    //throw an exception to cancel request
    @Throws(IOException::class)

    override fun intercept(chain: Interceptor.Chain): Response {

        val request = chain.request()
                .newBuilder() // returns Request.Builder
                .addHeader("Header_1", "value_1")
                .build()

        //proceed with the request
        return chain.proceed(request)
    }

}

Благодаря синтаксису анонимных функций Kotlin и шаблону Builder мы можем пропустить приведенные выше теоретические шаги и начать сборку.OkHttpClientкоторый имеет функцию.

      fun provideOkHttpClient(): OkHttpClient {
    
            //build client
            return OkHttpClient.Builder()
    
                    //create anonymous interceptor in the lambda and override intercept
                    // passing in Interceptor.Chain parameter
                    .addInterceptor { chain ->
                      //return response
                        chain.proceed(
                              //create request
                                chain.request()
                                        .newBuilder()
                                       //add headers to the request builder
                                        .also {
                                            it.addHeader("Header_1", "value_1")
                                            it.addHeader("Header_2", "value_2")
                                        }
                                        .build()
                        )
                    }
                    .also { okHttpClient ->.... }

В приведенном выше кодеaddInterceptor()открывает лямбду, где мы анонимно переопределяемintercept()передача параметра цепочки.

Мы используемchain.proceed(request)вернутьResponse. Именно при построении запроса на переход кchain.proceed() что мы изменим фактический запрос, чтобы добавить заголовки .

Вы также можете приступить к созданию OkHttpClient, чтобы добавить тайм-ауты и т. д.

      .also { okHttpClient ->

                    okHttpClient.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    okHttpClient.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)

                    if (BuildConfig.DEBUG) {
                        val httpLoggingInterceptor = HttpLoggingInterceptor().apply {

                            level = HttpLoggingInterceptor.Level.BODY
                        }

                        okHttpClient.addInterceptor(httpLoggingInterceptor)
                    }
                }
                .build()

Это окончательный код.

      @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {

        //build client
        return OkHttpClient.Builder()

                //create anonymous interceptor in the lambda and override intercept
                // passing in Interceptor.Chain parameter
                .addInterceptor { chain ->

                    //return response
                    chain.proceed(
                            //create request
                            chain.request()
                                    .newBuilder()

                                    //add headers to the request builder
                                    .also {
                                        it.addHeader("Header_1", "value_1")
                                        it.addHeader("Header_2", "value_2")
                                    }.build()

                    )
                }
                //add timeouts, logging
                .also { okHttpClient ->

                    okHttpClient.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    okHttpClient.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                        //log if in debugging phase
                    if (BuildConfig.DEBUG) {
                        val httpLoggingInterceptor = HttpLoggingInterceptor().apply {

                            level = HttpLoggingInterceptor.Level.BODY
                        }

                        okHttpClient.addInterceptor(httpLoggingInterceptor)
                    }
                }
                .build()


    }

Это самый длинный пост в истории StackOverflow, извините, ребята.

Насколько я вижу, вы передаете данные неправильно. Ваш метод getRestaurantsBySearch принимает два последних параметра в качестве поля заголовка, т.е. accept а также user-key, Но при вызове метода вы сначала передаете заголовки. Передайте данные, как вы заявили, в сигнатуре метода getRestaurantsBySearch

Пожалуйста, посмотрите на ответ. Это ясно показывает, что указанный вами ключ API неверен. Сначала вы получите правильный ключ API. Затем позвоните по запросу, он будет работать.

Другие вопросы по тегам