Call Retrofit2 + Decrypt + Json conversor - android

I am using retrofit2 in kotlin, and I need to get the content that is a json and this encrypted, I know that to convert json just use the JacksonConverterFactory (until this part was working well) but an encryption was added before that and I do not know how To handle this, do I need to create a converter of my own? Does anyone have a read to tell me?
My current call for retrofit
val retrofit = Retrofit.Builder()
.baseUrl("http://100.1.1.100/")
.addConverterFactory(JacksonConverterFactory.create())
.client(httpClient.build())
.build()
And i already have my fucntion (working) to decrypt:
CryptAES.decrypt(value))

This can be done by creating an decrypt interceptor:
class DecryptInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain
.run { proceed(request()) }
.let { response ->
return#let if (response.isSuccessful) {
val body = response.body()!!
val contentType = body.contentType()
val charset = contentType?.charset() ?: Charset.defaultCharset()
val buffer = body.source().apply { request(Long.MAX_VALUE) }.buffer()
val bodyContent = buffer.clone().readString(charset)
response.newBuilder()
.body(ResponseBody.create(contentType, bodyContent.let(::decryptBody)))
.build()
} else response
}
private fun decryptBody(content: String): String {
//decryption
return content
}
}
setup:
val httpClient = OkHttpClient().newBuilder()
httpClient.addInterceptor(DecryptInterceptor())
val retrofit = Retrofit.Builder()
.baseUrl("http://100.1.1.100/")
.addConverterFactory(JacksonConverterFactory.create())
.client(httpClient.build())
.build()

Related

Dynamic urls with Koin with Retrofit also call the old url

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,

Retrofit with dynamic URL

in my application am using a Retrofit 2.9.0, my issue is the user can change completely the URL from the app menu, in this case is not working when i changed the URL only if i restart the app.
this my instance of Retrofit :
object ApiService {
var token: String = ""
#JvmName("setToken1")
fun setToken(tk: String) {
token = tk
}
private val globalInterceptor = GlobalErrorInterceptor()
private val loginInterceptor = LoginErrorInterceptor()
private val okHttpClient =
OkHttpClient.Builder().addInterceptor(globalInterceptor).build()
private val okHttpClientLogin =
OkHttpClient.Builder().addInterceptor(loginInterceptor).build()
var gson = GsonBuilder()
.setLenient()
.create()
/**This instance for the others requests */
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(LOGIN_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build()
}
val API: WebServicesApi by lazy {
retrofit.create(WebServicesApi::class.java)
}
/**This instance for the login to get the Token */
private val retrofitLogin by lazy {
Retrofit.Builder()
.baseUrl(LOGIN_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClientLogin)
.build()
}
val APILogin: WebServicesApi by lazy {
retrofitLogin.create(WebServicesApi::class.java)
}
}
You can dynamically change retrofit URL by doing something like this. First change retrofit from val to var.
private fun changeBaseUrl(url: String) {
// change the base url only if new url is different than old url
if (retrofit.baseUrl().toString() != url) {
retrofit = retrofit.newBuilder().baseUrl(url).build()
}
}
Please note you might have to change this method and call it according to your flow. The main point to note here is the use of .newBuilder().baseUrl(url).build().

Retrofit auto parse html unicode

I have a problem. I have a json. I'm trying to parse symbols like ' and \u00e7. Symbol \u00e7 successfully parses, but symbol ' remains unchanged. Here's my retrofit builder.
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(RatersApi.BASE_URL)
.client(get())
.build()
.create(RatersApi::class.java)
and ok http builder which called from get() function
OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor()
.apply { level = HttpLoggingInterceptor.Level.BODY }
)
.addInterceptor(HeaderInterceptor())
.build()
ANSWER
Ok. I didn't find correct solution, so i wrote my own interceptor which transform strings from html. Just inject this into your okhttp builder Here it is:
class HtmlStringInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
val contentType = response.body?.contentType()
val bodyString = if (android.os.Build.VERSION.SDK_INT >= 24) {
Html.fromHtml(response.body?.string(), Html.FROM_HTML_MODE_COMPACT).toString()
} else {
Html.fromHtml(response.body?.string()).toString()
}
val body = bodyString.toResponseBody(contentType)
return response.newBuilder().body(body).build()
}
}
The cause of this issue is already in your title: ' is a HTML code and not Unicode.
So the JSON parsing is correct and you need additional processing to handle HTML content. For example if you want to show it in a TextView you can use the following:
// extension function to handle different api levels
fun TextView.setHtml(htmlContent: String) {
if (android.os.Build.VERSION.SDK_INT >= 24) {
this.text = Html.fromHtml(htmlContent, Html.FROM_HTML_MODE_COMPACT)
} else {
#Suppress("DEPRECATION")
this.text = Html.fromHtml(htmlContent)
}
}
// set view content from JSON
text_view.setHtml("test: '")

Retrofit2: Add post parameter into interceptor

Inside my Android kotlin app i'm calling some apis by using retrofit2 like
#FormUrlEncoded
#POST("something/some")
fun callMyApi(
#Field("myField") myField: String
): Deferred<MyResponseClass>
Now i need to add some common post params to all my api request (and keep the specific ones for each call, in this case i need to keep "myField"), so i'm using an interceptor:
val requestInterceptor = Interceptor { chain ->
val newRequest = chain.request()
.newBuilder()
.post(
FormBody.Builder()
.add("common1Key", "common1")
.add("common2Key", "common2")
.add("common3Key", "common3")
.build()
)
.build()
return#Interceptor chain.proceed(newRequest)
}
But this implementation fails because the interceptor seems to overwrite myField.
How can i fix it?
We can create Interceptor by using two or more common query parameter.
val requestInterceptor = Interceptor { chain ->
val url = chain.request()
.url()
.newBuilder()
.addQueryParameter("common1key", "common1")
.addQueryParameter("common2key", "common2")
.addQueryParameter("common3key", "common3")
.build()
val request = chain.request()
.newBuilder()
.url(url)
.build()
return#Interceptor chain.proceed(request)
}
I have added Interceptor for post form body.
interface PostWebApiService {
#POST("posts")
#FormUrlEncoded
fun savePost(
#Field("title") title: String
): Deferred<Post>
companion object {
operator fun invoke(): PostWebApiService {
val requestInterceptor = Interceptor { chain ->
var request = chain.request()
val requestBuilder = request.newBuilder()
val formBody = FormBody.Builder()
.add("body", "Body")
.add("userId", "12")
.build()
var postBodyString = bodyToString(request.body())
val concat = if (postBodyString.isNotEmpty()) "&" else ""
postBodyString = postBodyString + concat + bodyToString(formBody)
request = requestBuilder.post(
RequestBody.create(
MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"),
postBodyString
)
)
.build()
return#Interceptor chain.proceed(request)
}
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(requestInterceptor)
.build()
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("http://jsonplaceholder.typicode.com/")
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(PostWebApiService::class.java)
}
fun bodyToString(request: RequestBody?): String {
try {
var buffer = Buffer()
request?.writeTo(buffer)
return buffer.readUtf8()
} catch (e: IOException) {
return "error"
}
}
}
}

How to fetch data from network and not from http cache?

Here is my code , I have added CacheHeaderInterceptor but one of the requests
for some cases needs to do force call from network instead of retrieving cache response
but as I have added CacheHeaderInterceptor it never called after first call.
but I need to have check and based on that check fetch from network or retrieve cache response
#Singleton
#Provides
fun httpClient(context: Context, #Named(“UserPreferences”) preferences: SharedPreferences): OkHttpClient {
val appCacheDir = context.cacheDir
val httpCacheDir = File(appCacheDir, HTTP_CACHE_DIRNAME)
if (!httpCacheDir.exists()) {
httpCacheDir.mkdirs()
}
val builder = OkHttpClient.Builder()
val authInterceptor = LegacyAuthInterceptor(preferences, userAuthRelay)
builder.addNetworkInterceptor(authInterceptor)
if (BuildConfig.DEBUG) {
builder.addNetworkInterceptor(StethoInterceptor())
}
builder.addNetworkInterceptor(CacheHeaderInterceptor(isStoreUpdatedRelay))
return builder
.cache(Cache(httpCacheDir, MAX_HTTP_CACHE_SIZE))
.connectTimeout(30, SECONDS)
.writeTimeout(30, SECONDS)
.readTimeout(30, SECONDS)
.retryOnConnectionFailure(true)
.build()
}
class CacheHeaderInterceptor(private val isUpdatedRelay: BehaviorRelay<Boolean>) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
chain.request().headers().get(CustomCacheHeader.CUSTOM_CACHE_HEADER_KEY)
?: return chain.proceed(chain.request())
val maxAge = chain.request().headers().values(CustomCacheHeader.CUSTOM_CACHE_HEADER_KEY).firstOrNull()?.toLongOrNull()
val modifiedRequest = chain.request().newBuilder().removeHeader(CustomCacheHeader.CUSTOM_CACHE_HEADER_KEY).build()
val originalResponse = chain.proceed(modifiedRequest)
return when {
isUpdatedRelay.value -> {
val modifiedResponse = originalResponse.newBuilder()
.addHeader("Cache-Control", "no-cache")
.build()
isStoreUpdatedRelay.accept(false)
modifiedResponse
}
maxAge != null -> {
// Add Cache-Control to the response.
val modifiedResponse = originalResponse.newBuilder()
.removeHeader("Cache-Control")
.removeHeader("Pragma")
.addHeader("Cache-Control", "max-age=$maxAge")
.build()
modifiedResponse
}
else -> // Missing max-age, proceed with original response.
originalResponse
}
}
}
I found the solution for my question
adding addInterceptor()
There is addNetworkInterceptor() and addInterceptor().

Categories

Resources