60

I'm trying to call an API which requires me to pass in an API key.

My Service call using HttpURLConnection is working perfectly.

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

However, I'm not sure how this works with Retrofit as my call in going into Failure at all times. Here's the code I'm using for the same service call

@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);

and I'm using this to call it

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

All these calls are going into the OnFailure Method in RetroFit. If I send it without the HeaderParameters it goes into Success with a 403 because I obviously need to pass the api key somewhere but I cant figure out how.

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

The error I'm getting in OnFailure is

java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 2 path $
jamian
  • 1,244
  • 2
  • 15
  • 24

5 Answers5

93

Try this type header for Retrofit 1.9 and 2.0. For the JSON content type.

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

You can add many more headers, i.e,

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

Dynamically add to headers:

@POST("user/classes")
Call<ResponseModel> addToPlaylist(@Header("Content-Type") String content_type, @Body RequestModel req);

Call your method, i.e.,

mAPI.addToPlayList("application/json", playListParam);

Or

Want to pass every time, then create an HttpClient object with the HTTP Interceptor:

OkHttpClient httpClient = new OkHttpClient();
        httpClient.networkInterceptors().add(new Interceptor() {
            @Override
            public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
                Request.Builder requestBuilder = chain.request().newBuilder();
                requestBuilder.header("Content-Type", "application/json");
                return chain.proceed(requestBuilder.build());
            }
        });

Then add to a Retrofit object

Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).client(httpClient).build();

If you are using Kotlin, remove the { }. Else it will not work.

Peter Mortensen
  • 30,030
  • 21
  • 100
  • 124
Avinash Verma
  • 2,262
  • 16
  • 22
50

You can use the below

 @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);

and

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

The above is based in the zomato api which is documented at

https://developers.zomato.com/documentation#!/restaurant/search

Thing to note is the end point change api/v2.1/search and the Header @Headers("user-key: 9900a9720d31dfd5fdb4352700c").

Also check your base url .baseUrl("https://developers.zomato.com/")

Also i tried the above with a api key i generated and it works and my query was cafes as suggested the zomato documentation.

Note : I hope you have the below

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

and the below in build.gradle file

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

Edit:

You can also pass header with dynamic value as below

@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);

And

Call<String> call = endpoint.getRestaurantsBySearch("3","city","cafes","9900a9720d31dfd5fdb4352700c");
Raghunandan
  • 131,557
  • 25
  • 223
  • 252
6

After trying a couple of times i figured out the answer.

The error

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

was coming due the failure of parsing the json.

In the method call I was passing a String instead of a POJO class.

@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);

I should have passed instead of Call<String> the type of Call<Data>

Data being the Pojo class

something like this

@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);
jamian
  • 1,244
  • 2
  • 15
  • 24
  • it depends on what type of converter you use. Gson converter for parsing json. or use scalar converter for string. Since you had not mentioned nay of those i had put a note in my answer. Call is possible – Raghunandan Mar 23 '17 at 10:48
  • Using Kotlin, I get the error "An Annotation argument must be a compile time constant" – Peter Chaula Jun 22 '19 at 13:43
0

As far as i can see you are passing the data in a wrong way. Your method getRestaurantsBySearch is accepting the last two parameter as header field i.e accept and user-key. But while calling the method you are passing headers first. Pass the data as you have declared it in method signature of getRestaurantsBySearch

Nitin Joshi
  • 240
  • 2
  • 7
0

enter image description here

Please take a look at the response. It clearly shows that the api key you provided is wrong. At first you get the correct api key. Then call the request it will work .

Manoj Perumarath
  • 7,910
  • 6
  • 49
  • 67