I try to create kotlin app with OkHttp3. Can I use response.header("error") or response.header("success") in if()?
For example, if I got response with error I will show error or if I get success and I can start next activity to show in next activity header("username").
My request fun:
fun requestAuth(url: String) {
var param = FormBody.Builder()
.add("login", login.text.toString())
.add("password", password.text.toString())
.build()
val request = Request.Builder().url(url).post(param).build()
val client = OkHttpClient()
.newBuilder()
.build()
client.newCall(request).enqueue(object : Callback{
override fun onResponse(call: Call, response: Response) {
// startActivity(Intent(this#Login, NavigationMenu::class.java)
// .putExtra("session_token", response
// .header("session_token")
// .toString())
.putExtra("username", response.header("username").toString()))
}
Thank you for every suggestion
Related
I am using CronetEngine for making a network request as below
val engine: CronetEngine = CronetEngine.Builder(context).enableQuic(false)
.enableBrotli(true)
.enableHttp2(false).build()
val callFactory: Call.Factory = CronetCallFactory.newBuilder(engine).build()
And calling the result as
request = Request.Builder()
.url(url)
.post(formBody.build())
.headers(headers)
.build()
callFactory.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
result = response.body?.string()
mResponseCode = response.code
mResponseHeader = response.headers.toMultimap()
}
})
I am getting the response in compressed format.
Where as I want it in plain text or decompressed format.
Any configuration I am missing here?
I am attempting to make a sync call that needs to complete before proceeding with storing a user in the cloud. I believe the issue is within the RequestBody as it looks like it is just a byte array. Below is the code:
val client = OkHttpClient()
val mediaType: MediaType? = "application/json".toMediaTypeOrNull()
val body: RequestBody =
RequestBody.create(mediaType, "{\"type\":\"DEFAULT\",\"name\":\"lkjlkj\"}")
val request: Request = okhttp3.Request.Builder()
.url("https://api.example.com/endpoint")
.post(body)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.addHeader(
"Authorization",
"Bearer SK-xxxxxx-4QAXH"
)
.build()
Toast.makeText(this#RegisterActivity, "Entering Call",Toast.LENGTH_SHORT).show()
val response: Unit = client.newCall(request).execute().use {
Toast.makeText(this#RegisterActivity, "sent call, awaiting response",Toast.LENGTH_SHORT).show()
if (it.isSuccessful){
val content = JSONObject(it.body.toString())
desiredString = content.getJSONArray("desiredStringField").toString()
Toast.makeText(this#RegisterActivity, desiredString,Toast.LENGTH_SHORT).show()
}
if (!it.isSuccessful){
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
}
}
The code doesn't crash but it seems the call never completes, as it never makes it into the it.isSuccessful or !it.isSuccessful. Perhaps its a bad formed call somehow. Please help if you can.
Try to enqueue the request and manage the response using a Callback:
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful){
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
return
}
try {
val content = JSONObject(response.body?.string() ?: "")
desiredString = content.getJSONArray("desiredStringField").toString()
Toast.makeText(this#RegisterActivity, desiredString,Toast.LENGTH_SHORT).show()
} catch (e: JSONException) {
// Error parsing JSON object
}
}
override fun onFailure(call: Call, e: IOException) {
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
}
}
I'm working on a solution that needs to make recurring calls to an api every 10 seconds. However, I need to dynamically change the URL pointing to another service. That is, the new loop that will start will make the call to this new url base. I am using Koin as a DI. Here is an example of my code:
This is my dataModule koin
single<Retrofit>() {
Retrofit.Builder()
.client(httpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(OLD_BASE)
.build()
}
single<ApiService>() {
get<Retrofit>().create(ApiService::class.java)
}
{ single<OkHttpClient>(named(WITH_AUTH)) {
OkHttpClient.Builder()
.callTimeout(30, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(get<HttpLoggingInterceptor>(named(DATA_INTERCEPTOR)))
.addInterceptor(get<AuthInterceptor>(named(AUTH_INTERCEPTOR)))
.authenticator(get<AccessTokenAuthenticator>(named(AUTH_AUTHENTICATOR)))
.build()
}
single(named(DATA_INTERCEPTOR)) {
HttpLoggingInterceptor().apply {
level =
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.BASIC
}
}
}
single<AuthInterceptor>(named(AUTH_INTERCEPTOR)) {
AuthInterceptor(
get(), get()
)
}
And this is my interceptor :
class AuthInterceptor(
private val tokenRepository: TokenRepository,
private val envRepository: EnvRepository
) : Interceptor {
#Volatile
private var host: HttpUrl? = null
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val token = tokenRepository.getToken(TokenRepository.AUTH_TOKEN).blockingGet()
//val authenticationRequest = request(originalRequest, token)
host = envRepository.getEnvBaseUrl().toHttpUrlOrNull()
host?.let {
val newUrl = chain.request().url.newBuilder()
.scheme(it.scheme)
.host(it.toUrl().toURI().host)
.port(it.port)
.build()
request = chain.request().newBuilder()
.url(newUrl)
.build()
}
val authRequest = request(request, token) ?: request
return chain.proceed(authRequest)
}
private fun request(originalRequest: Request?, token: String?): Request? {
return if (!token.isNullOrEmpty()) {
originalRequest?.newBuilder()?.addHeader("Authorization", "Bearer $token")?.build()
} else {
originalRequest
}
}
}
The problem is that my interceptor works well, but each time before calling the new URL it also calls the old one. And I have no idea how to prevent it from calling the old URL in the loop. SO I have something like this in my debuger htts:
call old url
call olrd url
call new url
call new url
call old url
call old url
call new url
call new url
I hope I have been clear
Thanks,
This is the code I have for the refreshing token in an android app using kotlin ad retrofit 2.
The gradle :
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-moshi:retrofit:2.9.0"
And Authenticator is :
class OAuthAuthenticator(
private val refreshTokenService: Repository,
private val sessionManager: SessionManager
) : Authenticator {
#Synchronized
override fun authenticate(route: Route?, response: Response): Request? {
try {
//synchronized call to refresh the token
val refreshTokenResponse =
refreshTokenService.refreshJWTToken(sessionManager.getAuthTokens())
val sessionDataResponseBody = refreshTokenResponse.body()
if (refreshTokenResponse.isSuccessful && sessionDataResponseBody != null && !sessionDataResponseBody.jwt.isNullOrEmpty()) {
sessionManager.jwtToken = sessionDataResponseBody.jwt
// retry request with the new tokens (I get 400 error)
return response.request()
.newBuilder()
.addHeader("Authorization", "Bearer ${sessionManager.jwtToken}")
.build()
} else {
throw HttpException(refreshTokenResponse)
}
} catch (throwable: Throwable) {
when (throwable) {
is HttpException -> {
onSessionExpiration()
return null
}
}
}
return null
}
private fun onSessionExpiration() {
sessionManager.clear()
}
}
This is the Repository class :
object Repository {
fun refreshJWTToken(authTokens : AuthTokens) = RetrofitBuilder.userApi.getAuthenticationToken(authTokens).execute()
}
This is the API :
interface UserAPI {
#Headers("Cache-Control: no-cache")
#POST(AUTH_TOKENS_URL)
fun getAuthenticationToken(
#Header("Accept") accept : String,
#Header("Content-Type") contentType : String,
#Body params: AuthTokens
): Call<AuthTokenResponse>
}
The retrofit builder:
init {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val sessionManager = SessionManager.getInstance(context)
val httpLoggingInterceptor =
HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
httpClient = OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(ConnectivityCheckInterceptor(connectivityManager))
.addInterceptor(AuthInterceptor(sessionManager))
.authenticator(OAuth2Authenticator(UserRepository, sessionManager))
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
.build()
}
Question :
I can confirm that the code refreshes the Auth token and persists it successfully. However I get a 400 error after that. Any suggestions on what I am doing wrong?
I know this question is old, but for everyone who facing the same issue, it was just a simple mistake.
Please, use header(..., ...) instead of addHeader(..., ...) in the TokenAuthenticator class.
It worked for me.
I'm getting a 401, because token has expired, so I need to renew the token with another call and then do the call again, is there an easy way instead of doing :
disposable = loginService.login(
UserToLoginRequest(
input_email_login.text.toString(),
input_password_login.text.toString(),
generateRandomDeviceInfo()
)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
//It works
},
{ error -> if(error.code == 401) renewAccessToken() }
)
The thing is that I want to do something like this guy : Refreshing Oath token, but if it's possible to call again the same endpoint with the same parameters.
Example
getApple(1) <-- return info of apple id 1
The result is 401 <-- can't do the call without refreshing the accessToken refreshAccessToken()
Automatically call getApple(1) without disturbing the user
class Inceptor : Interceptor {
internal var token: String?=null
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request=chain.request()
request=request.newBuilder().build()
val response=chain.proceed(request)
if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
// get a new token (I use a synchronous Retrofit call)
val requestBody=FormBody.Builder()
.add("UserName", “abcd")
.add("Password", “*****")
.build()
val newRequest=request.newBuilder()
.url("Put your url")
.post(requestBody)
.build()
val tokenRefreshResponse=chain.proceed(newRequest)
val newRetryHttpUrl=request.url()
if (tokenRefreshResponse.code() == HttpURLConnection.HTTP_OK) {
val retryOriginaResponseBody: RequestBody
val builder=FormBody.Builder()
retryOriginaResponseBody=builder.build()
val retryRequest: Request
if (request.method() == "POST") {
retryRequest=request.newBuilder()
.url(request.url())
.post(retryOriginaResponseBody)
.build()
} else {
retryRequest=request.newBuilder()
.url(newRetryHttpUrl)
.build()
}
val retryResponse=chain.proceed(retryRequest)
return retryResponse
} else {
return tokenRefreshResponse
}
}
}
return response
}
}