I am new to retrofit. I need to use an API key to access this API to retrieve country data. I have placed my API key inside the header in place of MY_API_KEY.
interface CountryApi {
@Headers("api-key: MY_API_KEY")
@GET("all")
suspend fun getCountryData(): List<CountryDto>
}
I get the following error when I run the project.
2022-07-07 18:27:31.768 6035-6035/com.example.countryapp1 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.countryapp1, PID: 6035
retrofit2.HttpException: HTTP 401
at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:161)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
401 error indicates "No API was found" based on their API documentation. https://countryapi.io/documentation
I have also tried putting a "/" in front of "all", resulting in a 404 error.
Here is my dagger-hilt dependency injection module with the retrofit builder
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideApi(): CountryApi {
val retrofitBuilder = Retrofit.Builder()
.baseUrl("https://countryapi.io/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(CountryApi::class.java)
return retrofitBuilder
}
}
my app build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.countryapp1"
minSdk 23
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
packagingOptions {
resources {
excludes = '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation 'androidx.compose.material3:material3:1.0.0-alpha14'
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
implementation 'androidx.activity:activity-compose:1.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation "com.google.dagger:hilt-android:2.40.5"
kapt "com.google.dagger:hilt-android-compiler:2.40.5"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
}
CodePudding user response:
We can pass headers on retrofit builder. With that we dont have to mention it for each api methods.
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(chain -> {
Request.Builder builder = chain.request().newBuilder();
builder.addHeader("Authorization", "Bearer " token);
builder.addHeader("_UID", uid);
Request mainRequest = builder.build();
return chain.proceed(mainRequest);
});
Retrofit retrofit = new Retrofit.Builder().baseUrl(url)
.addConverterFactory(factory)
.client(httpClient.build())
.build();
CodePudding user response:
Some things we have to clear up regarding your question. I will be assuming that your api key is a constant, and not a piece of info that will be determined at runtime.
Authorization should be given in the header "Authorization: YOUR_API_KEY" as shown here https://countryapi.io/
401 errors doesn't mean "No API was found". A HTTP 401 error represents "Unauthorized", aka the server doesn't know who you are, and that you should give it your authorization key. This makes sense in your case, as you were giving your api key in the "api-key" header, instead of the "Authorization" hearer which the server was looking in.
The "/" at the beginning of the @GET annotation means that you are making the request from the root of the url. By having the declared base url as "https://countryapi.io/api/" and the @GET annotation as "/all", you are effectively making a request to "https://countryapi.io/all", which is probably not what you want. You either have to get rid of the "/" at the beginning of the @GET annotation, or change the base url to "https://countryapi.io/" and change the @GET annotation to "/api/all".
Here's a solution that you could implement
interface CountryApi {
@Headers("Authorization: MY_API_KEY") // Replace with your actual api key
@GET("all") // Note the lack of the '/' at the beginning of the get request
suspend fun getCountryData(): List<CountryDto> // Assuming that your data object is valid
}
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideApi(): CountryApi {
val retrofitBuilder = Retrofit.Builder()
.baseUrl("https://countryapi.io/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(CountryApi::class.java)
return retrofitBuilder
}
}