Добавить параметр заголовка в модификации
Я пытаюсь позвонить и 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. Затем позвоните по запросу, он будет работать.