Retrofit Interceptor not adding query param to URL - android

I'm trying to add apikey in the URL using custom interceptor but it's not adding the params in the URL so response body is null.
CustomInterceptor
class CustomInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url().newBuilder()
.addQueryParameter("apiKey", API_KEY)
.build()
val request = chain.request().newBuilder()
.url(url)
.build()
return chain.proceed(request)
}
}
Client
class Client {
companion object {
const val API_KEY = "123123"
private const val apiUrl = "https://www.omdbapi.com/"
fun <T> create(service: Class<T>): T {
val client = OkHttpClient.Builder()
.addInterceptor(CustomInterceptor())
.build()
return Retrofit.Builder()
.baseUrl(apiUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(service)
}
}
}
IMovie
interface IMovie {
#GET("/")
fun searchMovie(#Query("s") query: String): Call<SearchResult>
}
After sending the request the response body is null and this is the
Actual URL:- https://www.omdbapi.com/?s=Man
Expected URL:- https://www.omdbapi.com/?s=Man&apikey=123123

First create a new httpUrl instance from the existing request adding your query parameter and value:
var request = chain.request()
val httpUrl = request.url().newBuilder().addQueryParameter("token", authToken).build()
Then update the request:
request = request.newBuilder().url(httpUrl).build()
and proceed with it:
return chain.proceed(request)
When you recall the request from the chain (the one you proceed with after manipulation) you are getting the unmodified request again.

Related

how to refresh Bearer Token using retrofit android Kotlin

while login app i could get TOKEN, refresh token, and save it but i have no idea about when session end for token how to renew. how to add intercepter in it to renew token or some other method could be use ? here is my code
1- Api end point
#POST("identity/Identity/GetRefreshToken")
suspend fun refreshToken(): AuthTokenEntityDT0
2- AuthTokenEntityDT0 response
data class AuthTokenEntityDT0(
#SerializedName("accessToken") val accessToken: String,
#SerializedName("refreshToken") val refreshToken: String,
#SerializedName("statusCode") val statusCode: Int,
#SerializedName("statusMessage") val statusMessage: String,
)
3 - make request here
#Provides
#Singleton
fun provideAPIService(): ApiService {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.addHeader("Content-Type", "application/json; charset=UTF-8")
.addHeader("user-agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
.addHeader("Authorization", "Bearer $access_token")
val request = requestBuilder.build()
chain.proceed(request)
}
.build()
val retrofit = Retrofit.Builder()
.baseUrl(NativeBaseUrl.getBaseUrl())
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
return retrofit.create(ApiService::
class.java)
}
#Provides
fun provideApiRepository(apiService: ApiService): ApiRepository {
return ApiRepositoryImpl(apiService)
}
4 -
issue: refreshToken Api Calling repeatedly how i can stop it and
moved forward although i have put Dispatcher for one request
to move forward like this
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
val dispatcher = Dispatcher()
dispatcher.maxRequests = 1
val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(ForbiddenInterceptor(this))
.dispatcher(dispatcher)
.addInterceptor(interceptor)
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.addHeader("Content-Type", "application/json;
charset=UTF-8")
.addHeader("user-agent",
"Mozilla/5.0 (Macintosh; Intel Mac OS X
10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
.addHeader("Authorization", "Bearer $access_token")
val request = requestBuilder.build()
chain.proceed(request)
}
.build()
5- here is my intercepter
class ForbiddenInterceptor(var hIltModules: HIltModules) :
Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val response = chain.proceed(original)
if (response.code == 401) {
val responseRefreshTokens= runBlocking {
val originalRequests = chain.request()
val authenticationRequest = originalRequests.newBuilder()
.addHeader("refreshtoken", "${Constants.refreshToken}").build()
chain.proceed(authenticationRequest)
hIltModules.provideAPIService().refreshToken()
}
if (responseRefreshTokens.statusCode == 200) {
val originalRequests = chain.request()
val newAuthenticationRequest = originalRequests.newBuilder()
.removeHeader("refreshtoken")
.build()
access_token = responseRefreshTokens.accessToken
refreshToken = responseRefreshTokens.refreshToken
return chain.proceed(newAuthenticationRequest)
}
}
return chain.proceed(original)
}
}
I suggest you put your custom interceptor in its own class, like this:
class ForbiddenInterceptor : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val response: Response = chain.proceed(request)
if (response.code == 401) {
// this code section will run for every HTTP 401 response
}
return response
}
}
Then use this interceptor:
val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(ForbiddenInterceptor())
.build()
Adding the interceptor to the OkHttpClient makes sure you'll intercept the calls and you can run your custom code, check if it's been rejected by the server.
As for renewing your token. You'll probably need to implement another service of the backend. I can only assume, but you'll have to use your accesToken feed it into an endpoint, the server will return a fresh refreshToken.
here is my full code that i have done for refresh token it may be helpful for some one
class AuthenticationInterceptorRefreshToken #Inject constructor(
var hIltModules: HIltModules,
) : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val response = chain.proceed(originalRequest)
if (response.code == 401) {
synchronized(this) {
val originalRequest = chain.request()
val authenticationRequest = originalRequest.newBuilder()
.addHeader("refreshtoken", " $refreshToken")
.build()
val initialResponse = chain.proceed(authenticationRequest)
when (initialResponse.code) {
401 -> {
val responseNewTokenLoginModel = runBlocking {
hIltModules.provideAPIService().refreshToken()
}
when (responseNewTokenLoginModel.statusCode) {
200 -> {
refreshToken = responseNewTokenLoginModel.refreshToken
access_token = responseNewTokenLoginModel.accessToken
val newAuthenticationRequest = originalRequest.newBuilder()
.header("refreshtoken",
" $refreshToken")
.build()
return chain.proceed(newAuthenticationRequest)
}
else -> {
return null!!
}
}
}
else -> return initialResponse
}
}
}; return response
}

When I use retrofit2, the app is just shut down

I used retrofit2 to call API. But, when I called it, my app was just shut down. There are not errors in the Logcat. I googled it, but there is not a solution.
And Retrofit2 and converter-gson version is 2.9.0. I set internet permission in AndroidManifest.xml
MainActivity.kt
private fun loadMembers() {
val retrofit = Retrofit.Builder()
.baseUrl(MemberAPI.base_domain)
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitService = retrofit.create(MemberInterface::class.java)
retrofitService
.getMember(Constants.api_key)
.enqueue(object: Callback<Member> {
override fun onResponse(call: Call<Member>, response: Response<Member>) {
val members = response.body() as Member
var names = ""
for (member in members.response.body.items.item) {
names += "\n${member.empNm}"
}
binding.text.text = names
}
override fun onFailure(call: Call<Member>, t: Throwable) {
Toast.makeText(baseContext, "실패", Toast.LENGTH_LONG).show()
}
})
}
Interface.kt
interface MemberInterface {
#GET("{api_key}&numOfRows=5&pageNo=1&_type=json")
fun getMember(#Path("api_key") Key: String): Call<Member>
}
Youtube
Video
Try to add an interceptor so you can see all calls logs (headers, body, URLs, etc...). The crash could be related with the parse of the JSON response to the object Member.
Add OkHtpp to your grade dependencies:
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
And after that, when you create your Retrofit instance, add the interceptor, should look something like this:
val httpClient = OkHttpClient.Builder()
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
httpClient.addInterceptor(interceptor)
httpClient.addInterceptor(Interceptor { chain: Interceptor.Chain ->
val original: Request = chain.request()
val request: Request = original.newBuilder()
.header("Content-Type", "application/json")
.method(original.method, original.body)
.build()
chain.proceed(request)
})
val okHttpClient = httpClient.build()
val retrofit = Retrofit.Builder()
.baseUrl(MemberAPI.base_domain)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()

Special character = gets converted into %3D after calling API using retrofit

I want to pass = sign in request params as string. It coverts into %3D upon calling api.
You need to use custom interceptor as below:
class MyInterceptor: Interceptor {
override fun intercept (chain: Interceptor.Chain): Response {
val request = chain.request()
val unEncodedStringUrl = request.url.toString().replace("%3D", "=")
var newRequest = request.newBuilder().url(unEcodedStringUrl).build()
return chain.proceed(newRequest)
}
}
And then use it for OkHttpClient of the retrofit:
fun getOkHttpClient(
myInterceptor: MyInterceptor,
loggingInterceptor: HttpLoggingInterceptor
): OkHttpClient {
return OkHttpClient().newBuilder().addInterceptor(myInterceptor).also {
if (BuildConfig.DEBUG) {
it.addInterceptor(loggingInterceptor)
}
}.build()
}
Finally, use this OkHttpClient for retrofit as below:
fun getRetrofit(moshi: Moshi): Retrofit {
return Retrofit.Builder()
.baseUrl(StaticConstants.baseUrl)
.client(getOkHttpClient())
.addConverterFactory(
MoshiConverterFactory.create(moshi)
)
.build()
}

Remove header for a specific retrofit request in Android

Is there a way to remove a specific header after setting this kind of Interceptor :
public class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request()
val request: Request = original.newBuilder()
.addHeader(AppConstant.HEADER_APP_TOKEN, AppConfig.apptoken) //<-- need to remove this one for only one request
.addHeader(AppConstant.HEADER_SECURITY_TOKEN, AppConfig.security_token)
.method(original.method(), original.body())
.build()
return chain.proceed(request)
Here is my Retrofit instance :
object RetrofitClientInstance {
private val httpClient = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor())
.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
private val retrofit = Retrofit.Builder()
.baseUrl(AppConstant.SERVER_BETA)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build()
fun <T> getRetrofitInstance(service: Class<T>): T {
return retrofit.create(service)
}
}
And this is my API Service :
interface ApiService {
#GET("/app/shoes")
fun getShoes() : Call<Shoes>
}
Thanks for your help :)
Do it the other way around. Add a header to API calls indicating whether to add auth headers or not. Like so:
interface ApiService {
#Headers("isAuthorizable: false")
#GET("/app/Socks")
fun getShoes() : Call<Socks>
#GET("/app/shoes")
fun getShoes() : Call<Shoes>
#GET("/app/sandals")
fun getShoes() : Call<Sandals>
}
Check the header in the Interceptor and add header if condition is satisfied. Like so:
public class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request()
val shouldAddAuthHeaders = original.headers["isAuthorizable"] != "false"
val requestBuilder = request
.newBuilder()
.method(request.method, request.body)
.removeHeader("isAuthorizable")
if (shouldAddAuthHeaders) {
requestBuilder.addHeader(AppConstant.HEADER_APP_TOKEN, AppConfig.apptoken)
.addHeader(AppConstant.HEADER_SECURITY_TOKEN, AppConfig.security_token)
}
return chain.proceed(requestBuilder.build())
}
}
Note: Using the same logic, one may specify requests for which auth headers are to be added instead of filtering out requests that do not require authentication. That is,
interface ApiService {
#GET("/app/Socks")
fun getShoes() : Call<Socks>
#Headers("isAuthorizable: true")
#GET("/app/shoes")
fun getShoes() : Call<Shoes>
#Headers("isAuthorizable: true")
#GET("/app/sandals")
fun getShoes() : Call<Sandals>
}
public class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request()
val shouldAddAuthHeaders = original.headers["isAuthorizable"] == "true"
val requestBuilder = request
.newBuilder()
.method(request.method, request.body)
.removeHeader("isAuthorizable")
if (shouldAddAuthHeaders) {
requestBuilder.addHeader(AppConstant.HEADER_APP_TOKEN, AppConfig.apptoken)
.addHeader(AppConstant.HEADER_SECURITY_TOKEN, AppConfig.security_token)
}
return chain.proceed(requestBuilder.build())
}
}

Retrofit2 Authorization - Global Interceptor for access token

I'm trying to use Retrofit2,
I want to add Token to my Header Like this:
Authorization: Bearer Token
but the code below doesn't work:
public interface APIService {
#Headers({"Authorization", "Bearer "+ token})
#GET("api/Profiles/GetProfile?id={id}")
Call<UserProfile> getUser(#Path("id") String id);
}
My server is asp.net webApi. Please help what should I do?
You have two choices -- you can add it as a parameter to your call --
#GET("api/Profiles/GetProfile?id={id}")
Call<UserProfile> getUser(#Path("id") String id, #Header("Authorization") String authHeader);
This can be a bit annoying because you will have to pass in the "Bearer" + token on each call. This is suitable if you don't have very many calls that require the token.
If you want to add the header to all requests, you can use an okhttp interceptor --
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + token)
.build();
return chain.proceed(newRequest);
}
}).build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(/** your url **/)
.addConverterFactory(GsonConverterFactory.create())
.build();
If you want to add Bearer Token as a Header you can do those types of process.
This is one way to work with Bearer Token
In your Interface
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET("api/Profiles/GetProfile")
Call<UserProfile> getUser(#Query("id") String id, #Header("Authorization") String auth);
After that you will call the Retrofit object in this way
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("your Base URL")
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService client = retrofit.create(APIService.class);
Call<UserProfile> calltargetResponse = client.getUser("0034", "Bearer "+token);
calltargetResponse.enqueue(new Callback<UserProfile>() {
#Override
public void onResponse(Call<UserProfile> call, retrofit2.Response<UserProfile> response) {
UserProfile UserResponse = response.body();
Toast.makeText(this, " "+response.body(), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<UserProfile> call, Throwable t) {
//Toast.makeText(this, "Failed ", Toast.LENGTH_SHORT).show();
}
});
Another Way is using intercept, which is similar the previous Answer. But, that time you just need to modify interface little bit like.
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET("api/Profiles/GetProfile")
Call<UserProfile> getUser(#Query("id") String id);
Hope this will work for you.
Based on #iagreen solution kotlin version with different classes and structure suggested by #Daniel Wilson
Make Retrofit instance like this
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://yoururl"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(ServiceInterceptor())
//.readTimeout(45,TimeUnit.SECONDS)
//.writeTimeout(45,TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
Add ServiceInterceptor class like below
class ServiceInterceptor : Interceptor{
var token : String = "";
fun Token(token: String ) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if(request.header("No-Authentication")==null){
//val token = getTokenFromSharedPreference();
//or use Token Function
if(!token.isNullOrEmpty())
{
val finalToken = "Bearer "+token
request = request.newBuilder()
.addHeader("Authorization",finalToken)
.build()
}
}
return chain.proceed(request)
}
}
Login Interface and data class implementation
interface Login {
#POST("Login")
#Headers("No-Authentication: true")
fun login(#Body value: LoginModel): Call<LoginResponseModel>
#POST("refreshToken")
fun refreshToken(refreshToken: String):
Call<APIResponse<LoginResponseModel>>
}
data class LoginModel(val Email:String,val Password:String)
data class LoginResponseModel (val token:String,val
refreshToken:String)
call this in any activity like this
val service = RetrofitClientInstance.retrofitInstance?.create(Login::class.java)
val refreshToken = "yourRefreshToken"
val call = service?.refreshToken(refreshToken)
call?.enqueue(object: Callback<LoginResponseModel>{
override fun onFailure(call: Call<LoginResponseModel>, t: Throwable) {
print("throw Message"+t.message)
Toast.makeText(applicationContext,"Error reading JSON",Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<LoginResponseModel>, response: Response<LoginResponseModel>) {
val body = response?.body()
if(body!=null){
//do your work
}
}
})
for detail this video will be helpful.
This adds your token to the builder and you can change it at any time in case of login/logout.
object ApiService {
var YOUR_TOKEN = ""
private var retrofit: Retrofit = Retrofit.Builder()
.baseUrl("YOUR_URL")
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient.Builder().addInterceptor { chain ->
val request = chain.request().newBuilder().addHeader("Authorization", "Bearer ${YOUR_TOKEN}").build()
chain.proceed(request)
}.build())
.build()
var service: AppAPI = retrofit.create(AppAPI::class.java)
private set
}
You will need to add an Interceptor into the OkHttpClient.
Add a class called OAuthInterceptor.
class OAuthInterceptor(private val tokenType: String, private val accessToken: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", "$tokenType $accessToken").build()
return chain.proceed(request)
}
}
Following that, when you initialise your RetrofitApiService interface, you will need this.
interface RetrofitApiService {
companion object {
private const val BASE_URL = "https://api.coursera.org/api/businesses.v1/"
fun create(accessToken: String): RetrofitApiService {
val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor("Bearer", accessToken))
.build()
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(client)
.build()
return retrofit.create(RetrofitApiService::class.java)
}
}
}
Shout out to Java Code Monk, and visit the reference link for more details.
https://www.javacodemonk.com/retrofit-oauth2-authentication-okhttp-android-3b702350
The best approach is to use the new Authenticator API.
class TokenAuthenticator : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (response.request.header("Authorization") != null) {
return null
}
return response.request.newBuilder().header("Authorization", "Bearer " + token).build()
}
}
OkHttpClient.Builder().authenticator(TokenAuthenticator()).build()
Reference: https://square.github.io/okhttp/recipes/#handling-authentication-kt-java

Categories

Resources