Cannot make Post request in Retrofit Android (Kotlin) - android

I've been developing an Android Q&A app using Jetpack Compose. I've been trying to make Post requests in Retrofit but the data I send isn't on my API website. I've succeeded in making Get requests though. I've read many documents but I cannot find out what is wrong with this code.
This is data class.
data class UsersEntity(
val id: Int? = null,
val name: String? = null,
val uid: String? = null
)
This is Service interface.
interface UserService {
#POST("createusers")
fun createUsers(#Body usersinfo: UsersEntity): Call<Unit>
}
When I click a button, I'd like to send data to the server. I get the log "Hi, good job" but I cannot see the data on my API.
Button(
onClick = {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.*****.com/")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
val service: UserService = retrofit.create(UserService::class.java)
val usersInfo = UsersEntity(
3, "Alex", "164E92FC-D37A")
service.createUsers(usersInfo).enqueue(object: Callback<Unit> {
override fun onResponse(call: Call<Unit>, response: Response<Unit>) {
Log.d("Hi", "good job")
}
override fun onFailure(call: Call<Unit>, t: Throwable) {
Log.d("Hi", "error")
}
})
}
I changed the code like this.
Button(
onClick = {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.*****.com/")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
thread {
try {
val service: UserService = retrofit.create(UserService::class.java)
val usersInfo = UsersEntity(
3, "Alex", "164E92FC-D37A")
service.createUsers(usersInfo).enqueue(object: Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
Log.d("Response", "${response.body()}")
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.d("Hi", "error")
}
})
} catch (e: Exception) {
Log.d("response", "debug $e")
}
}
},
Could someone help me? Thank you.

I think your baseurl shouldn't end with a slash. Try this.
.baseUrl("https://api.*****.com")
And for your interface (also the Call<ResponseBody>):
interface UserService {
#POST("/createusers/")
fun createUsers(#Body usersinfo: UsersEntity): Call<ResponseBody>
}
Got some issues with this in the past so this might help. If not it atleasts cleans the code a bit :p
Also you can use ProxyMan to intercept your request and read what your application is actually sending to the server, might be a issue to find there!
Proxyman.io

Related

How to Parse Json in Kotlin Using Retrofit?

i am new to kotlin and i am in learning phase. I have followed many links but didn't able to understand completely.
I want Json response to show in my textview.
Problem: 1
I have tried this code but was unable to get data, but i want to get the items inside data object. Quote and author are coming null.
{
"status": 200,
"message": "Success",
"data": {
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
},
"time": "0.14 s"
}
Problem: 2
I dont know how to parse this response in textview
object ServiceBuilder {
private val client = OkHttpClient.Builder().build()
private val retrofit = Retrofit.Builder()
.baseUrl("https://url.com.pk/") // change this IP for testing by your actual machine IP
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}}
RestApi
interface RestApi{
#Headers("Content-Type: application/json")
#POST("api/getquotes")
abstract fun addUser(#Body userData: UserInfo): Call<UserInfo>}
RestAPiService
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<UserInfo>
{
override fun onFailure(call: Call<UserInfo>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<UserInfo>, response: Response<UserInfo>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser)
}
}
)
}
}
UserInfo
data class UserInfo (
#SerializedName("Quote")
val quote : String,
#SerializedName("Author")
val author : String
)
MainActivity
fun getQuotes() {
val apiService = RestApiService()
val userInfo = UserInfo("","")
apiService.addUser(userInfo) {
Log.d("Error registering user","errter")
/*if ( != null)
{
// it = newly added user parsed as response
// it?.id = newly added user ID
} else {
Log.d("Error registering user","errter")
}*/
}
}
Any help would be appreciated :)
Status, message and data are all part of the response so you need to take care of that. For example this
data class AddUserResponse(
val `data`: UserInfo, //like you defined it
val message: String,
val status: Int,
val time: String
)
This means parameter and response are different so the RestApi needs to be changed to this
abstract fun addUser(#Body userData: UserInfo): Call<AddUserResponse>}
This in turn also change the types in the service like
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<AddUserResponse>
{
override fun onFailure(call: Call<AddUserResponse>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<AddUserResponse>, response: Response<AddUserResponse>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser.data)
}
}
)
}
}
now in getQuotes you will have that it is a UserInfo object
apiService.addUser(userInfo) {
val returnedUserInfo = it
}
just follow my steps :
File->settings->Plugins
search for JSON To Kotlin class and install it
again click on File->New->Kotlin Data class from JSON
paste your json code here and click on generate. It will generate POJO classes and you will good to go.
The first thing I noticed, is that the data in your json is:
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
While your UserInfo defined #SerializedName("message") for Quote.

how to post with form-data only with retrofit

I try to post data and get the response with POST using retrofit. In postman it works fine but in code it error.
this is the last thing I tried
service
#FormUrlEncoded
#POST("program/get/list-user-program")
fun getProgram(
#Field("comunity") comunity:String,
#Field("programGroupUrl") programGroupUrl:Int
): Call<ListProgramResponse>
activity
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(URL_BASE)
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService: Service = retrofit.create(Service::class.java)
val call: Call<ListProgramResponse> = apiService.getProgram("abc", 0)
call.enqueue(object : Callback<ListProgramResponse> {
override fun onResponse(call: Call<ListProgramResponse>, response: Response<ListProgramResponse>) {
Log.e("check", response.body()?.toString())
override fun onFailure(call: Call<ListProgramResponse>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
t.printStackTrace()
}
})
response
data class ProgramBersedekahResponse(
#SerializedName("banner") val banner: String?,
#SerializedName("domainlembaga") val domainlembaga: String?,
#SerializedName("domainprogram") val domainprogram: String?,
#SerializedName("donate") val donatur: Int?
)
but it says java.lang.NumberFormatException: empty String and failure. did anyone know why? please help

How to run retrofit in retrofit?

I'm making cryptocurrency information viewer using Retrofit. Everything is okay, but have a problem.
One exchange demands me execute retrofit twice.
https://api.upbit.com/v1/market/all
https://api.upbit.com/v1/ticker?markets=KRW-BTC,KRW-ETH,KRW-BTG,KRW-BSV,KRW-ETC ..., KRW-(Some cryptocurrency)
Base url is "https://api.upbit.com/v1/". And I have to get 'markets' from first api, and get cryptocurrency's information by using 'markets' as query value.
But there are problem. I thought first and second execution. Get markets from first execution, and get infomations by using markets. But Retrofit basically runs asynchronously, So usually skip first execution and there are no markets in second executions. (markets = "")
How can I run Retrofit in Retrofit? Did I approach wrong? Or there are solution for this problem?
This is Retrofit parts in my code.
var markets = ""
val publishSubject: PublishSubject<String> = PublishSubject.create()
init {
publishSubject.subscribe {
markets = it
}
}
fun getData(exchange: String) {
// var markets = "Basic"
val url = when(exchange) {
coinone -> "https://api.coinone.co.kr/"
bithumb -> "https://api.bithumb.com/"
upbit -> {
getMarketsUpbit()
"https://api.upbit.com/v1/"
}
else -> "https://api-cloud.huobi.co.kr/"
}
val parser = DataParser()
val builder = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RetrofitService::class.java)
val call: retrofit2.Call<Any> =
when (exchange) {
coinone ->
builder.getTickersCoinone("all")
bithumb ->
builder.getTickersBithumb()
upbit ->
builder.getTickersUpbit(markets)
else ->
builder.getTickersHuobi()
}
call.enqueue(object : retrofit2.Callback<Any> {
override fun onResponse(call: retrofit2.Call<Any>, response: retrofit2.Response<Any>) {
coinInfos.value = parser.getParsedData(
if (exchange != "upbit") exchange
else markets
,
response.body().toString()
)
}
override fun onFailure(call: retrofit2.Call<Any>, t: Throwable) {
println("Retrofit process is failed.")
}
})
}
private fun getMarketsUpbit() {
val parser = DataParser()
var markets = ""
val builder = Retrofit.Builder()
.baseUrl("https://api.upbit.com/v1/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RetrofitService::class.java)
val call: retrofit2.Call<Any> = builder.getMarketsUpbit()
call.enqueue(object : retrofit2.Callback<Any> {
override fun onResponse(call: retrofit2.Call<Any>, response: retrofit2.Response<Any>) {
publishSubject.onNext(parser.parseUpbitMarkets(response.body().toString()))
}
override fun onFailure(call: retrofit2.Call<Any>, t: Throwable) {
println("Retrofit process is failed.")
}
})
}

Why I get Unable to create converter exception from retrofit?

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.

Android get data from dynamic url from APi

I found some Api about Pokemon(https://pokeapi.co). And I try get data from this Api and it's work.
The data look like this
But I try to use name of data from Api to get some image with this path "api/v2/pokemon/{name}"
The problem is how can get name out of onResponse or do the other way to get name and image of Pokemon
MainActivity
val retrofit = Retrofit.Builder()
.baseUrl("https://pokeapi.co/")
.addConverterFactory(GsonConverterFactory.create())
.client(HTTPLogger.getLogger())
.build()
val jsonPlaceholderApi = retrofit.create(pokemonService::class.java)
val myCall: Call<PokemonInGen> = jsonPlaceholderApi.getGen(1)
myCall.enqueue(object : Callback<PokemonInGen> {
override fun onResponse(
call: Call<PokemonInGen>,
response: Response<PokemonInGen>
) {
val DataResponse: PokemonInGen = response.body()!!
Timber.i("on do Respon %s", DataResponse)
}
override fun onFailure(call: Call<PokemonInGen>, t: Throwable) {
Timber.i("on do ERROR")
}
})
My Service
interface pokemonService {
#GET("api/v2/generation/{id}")
fun getGen(
#Path("id") id: Int,
): Call<PokemonInGen>
#GET("api/v2/pokemon/{name}")
fun getArtwork(
#Path("name") name: String,
): Call<PokemonArtwork>
}
My Model Data class
data class PokemonInGen(
val pokemon_species: List<PokemonList>)
data class PokemonList(
val name: String,
val url: String,
)
To fetch Pokemon image you should create additional function in your MainActivity class
fun fetchPokemonArtwork(name: String) {
jsonPlaceholderApi.getArtwork(name).enqueue(object : Callback<PokemonArtwork> {
override fun onResponse(
call: Call<PokemonArtwork>,
response: Response<PokemonArtwork>
) {
// An artwork is successful fetched
val artwork = response.body()!!
}
override fun onFailure(call: Call<PokemonArtwork>, t: Throwable) {
// Handle a failure
}
})
}
You should call this function right after you fetched a Pokemon in generation
val myCall: Call<PokemonInGen> = jsonPlaceholderApi.getGen(1)
myCall.enqueue(object : Callback<PokemonInGen> {
override fun onResponse(
call: Call<PokemonInGen>,
response: Response<PokemonInGen>
) {
val DataResponse: PokemonInGen = response.body()!!
Timber.i("on do Respon %s", DataResponse)
// Extract name
val name = DataResponse.pokemon_species.first().name
// Fetch an artwork
fetchPokemonArtwork(name)
}
override fun onFailure(call: Call<PokemonInGen>, t: Throwable) {
Timber.i("on do ERROR")
}
})
P.S. I proceeded from the assumption that you've implemented PokemonArtwork class. Please let me know if you are facing difficulties in comments below.
P.S.S. It's not recommended to make network calls in Activity or Fragment classes. This guide to app architecture should help you to select correct app structure in your future releases.
You can using #Url to support dynamic link in retrofit. Example below:
interface pokemonService {
#GET
Call<PokemonResponse> getListPokemon(#Url String url);
}
And paste your url to browser to see data format.

Categories

Resources