I found a youtube video on how to do a get url but I need to do a post to a REST api. I am unsure on how to do so.
Ive tried to look around on here but most of it is java.
fun fetchJson() {
println ("attempting to fetch JSON")
val url = "https://utgapi.shift4test.com/api/rest/v1/transactions/sale"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onResponse(call: Call?, response: Response?) {
val body = response?.body()?.string()
println(body)
println("try and get json api working there was an error")
}
override fun onFailure(call: Call, e: IOException) {
println("failed to execute request")
}
with the GET i just receive a error because i am not doing a POST request.
Found something here https://stackoverflow.com/a/29795501/5182150
Converting it to kotlin would be like
private val client = OkHttpClient();
fun run() throws Exception {
val formBody = FormEncodingBuilder()
.add("search", "Jurassic Park")
.build() as RequestBody;
val request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(formBody)
.build();
val response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
This should be the gist. You can deal with nullable issues as the studio gives you warnings.
Another tip is You can use Retrofit too which works using OkHTTP for network calls. You can find more about retrofit here https://square.github.io/retrofit/ and a good tutorial here https://medium.com/#prakash_pun/retrofit-a-simple-android-tutorial-48437e4e5a23
If you are using OkHttp you can check to this code
fun POST(url: String, parameters: HashMap<String, String>, callback: Callback): Call {
val builder = FormBody.Builder()
val it = parameters.entries.iterator()
while (it.hasNext()) {
val pair = it.next() as Map.Entry<*, *>
builder.add(pair.key.toString(), pair.value.toString())
}
val formBody = builder.build()
val request = Request.Builder()
.url(url)
.post(formBody)
.build()
val call = client.newCall(request)
call.enqueue(callback)
return call
}
Related
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
}
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()
My problem is, when I use Call inside my retrofit api, I get this exception:
Unable to create converter for retrofit2.Call<java.util.List>
for method IntegApi.getAllProductBarcodeAsync
The intersting thing, if I don't use Call then the error message is gone.
I want to use Call, becuse I would like to use a custom response class, because i want the know when the api sends 404 status, and then I want to skip this exception.
I use Moshi to convert Json
Sync function:
private suspend fun syncProductBarcodes() {
try {
val productBarcodes = api.getAllProductBarcodeAsync().await()
if(productBarcodes.isSuccessful) {
productBarcodeRepository.deleteAndInsertAll(productBarcodes.body() ?: emptyList())
addOneToStep()
}
}catch (e: Exception){
Timber.d(e)
throw e
}
}
Api:
#GET("Product/GetAllBarcode")
suspend fun getAllProductBarcodeAsync(): Call<List<ProductBarcode>>
Entity class:
#Entity(
tableName = ProductBarcode.TABLE_NAME
)
#JsonClass(generateAdapter = true)
class ProductBarcode(
#PrimaryKey
#ColumnInfo(name = "id")
#Json(name = "Id")
val id: String = "",
#ColumnInfo(name = "product_id", index = true)
#Json(name = "ProductId")
var ProductId: String = "",
#ColumnInfo(name = "barcode", index = true)
#Json(name = "Barcode")
var barcode: String = ""
) {
companion object {
const val TABLE_NAME = "product_barcode"
}
}
ExtensionFun:
suspend fun <T> Call<T>.await(): Response<T> = suspendCoroutine { continuation ->
val callback = object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
override fun onResponse(call: Call<T>, response: Response<T>) =
continuation.resumeNormallyOrWithException {
if (response.isSuccessful || response.code() == 404) {
return#resumeNormallyOrWithException response
} else {
throw IllegalStateException("Http error ${response.code()}, request:${request().url()}")
}
}
}
enqueue(callback)
}
ApiModule:
#Provides
#Singleton
fun provideMoshi(): Moshi {
return Moshi.Builder()
.add(DateConverter())
.add(BigDecimalConverer())
.add(KotlinJsonAdapterFactory())
.build()
}
fun provideIntegApi(
#Named("base_url") url: String, moshi: Moshi,
prefManager: PrefManager
): IntegApi {
var builder = OkHttpClient.Builder()
builder = BuildTypeInitializations.setupInterceptor(builder)
builder.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${prefManager.token}")
.addHeader("Connection", "close")
.build()
val response = chain.proceed(request)
return#addInterceptor response
}
.readTimeout(5, TimeUnit.MINUTES)
.writeTimeout(5, TimeUnit.MINUTES)
.connectTimeout(5, TimeUnit.MINUTES)
.retryOnConnectionFailure(true)
return Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.client(builder.build())
.build()
.create(IntegApi::class.java)
}
I don't know exactly why, but If the Api send 404 response, after than I get the Unable to crate converter exception, but I the server send the response, the error message is gone.
Update:
If is use this :
#get:GET("Product/GetAllBarcode")
val getAllProductBarcodeAsync: Call<List<ProductBarcode>>
instead of this:
#GET("Product/GetAllBarcode")
suspend fun getAllProductBarcodeAsync(): Call<List<ProductBarcode>>
There won't be error, and everything works fine, but I don't understand what's the problem
Update2
I changed Moshi to Jackson, and it doesn't throw converter error like moshi, but throw Http 404 Error which is more friendlier for me, but I' m not completely satisfied. I created await() fun because of Http 404 errors, and I think this bunch of code skipped because of http 404?
Finally I found the issue, I made a mistake, because I tried to use suspend fun and retrofit Call at the same time. I deleted the suspend keyword, and it works perfectly.
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.
I need to make a post request to an endpoint from my android project. I am using Kotlin. Will I need to create a separate OKHttpClient Class for this.
Here's how you can achieve this:
val payload = "test payload"
val okHttpClient = OkHttpClient()
val requestBody = payload.toRequestBody()
val request = Request.Builder()
.method("POST", requestBody)
.url("url")
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// Handle this
}
override fun onResponse(call: Call, response: Response) {
// Handle this
}
})
Don't forget to import:
import okhttp3.RequestBody.Companion.toRequestBody