How to properly use Retrofit to call a JSON API in Kotlin - android

I've tried following several tutorials on calling an API and receiving a JSON response in Kotlin, and this is what I have so far:
interface APIService {
#GET("cursor/popular/10")
fun listRepos(): Call<Any?>
// #Path("user") user: String?
}
fun getURL(url: String): Call<Any?> {
val retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(APIService::class.java)
val data: Call<Any?> = service.listRepos()
Log.d("PRINT_JSON_HERE", "HERE: ${data}")
return data
}
When I call getURL("api_here") nothing happens, no error either.
Just wondering what I am doing wrong. I know it says the data class is "Any" but when I start getting a response I'll replace it with a proper data class.
This code is inside a class/ViewModel(). What am I doing wrong?

Api call returns a response and you need to implement onResponse and onFailure override method in your program after
val service = retrofit.create(APIService::class.java)
this put this code
service.enqueue(object : Callback<List<userItem>> {
override fun onResponse(
call : Call<List<userItem>>,
response: Response<List<userItem>>
) {
var data = response.body()
Log.d("data", data.toString)
}
override fun onFailure(call : Call<List<userItem>> , t : Throwable) {
"print toast if an error occurred"
}
)}
}
}
in this code userItem is a data class that can get data from the api.
I hope this can help you.
):

Related

Cannot make Post request in Retrofit Android (Kotlin)

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

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 get response headers using Retrofit in Kotlin?

Hello I am working on async using retrofit and rxjava2
and I have to get the value from the header while talking to the server developer.
However, I don't know how to get the header from the method I use. I know how to get it from Call Response, but I don't know how to bring the header because the method used is different.
my retrofit2 class
private val retrofit: Retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client())
.baseUrl(serverIp)
.build()
val userApi: UserAPI = retrofit.create(UserAPI::class.java)
my model class
#POST("user")
fun login(
#Body loginRequest : LoginRequest
) : Single<UserResponse>
data class LoginRequest(
val phone: String?,
#SerializedName("gender")
val gender: String?,
#SerializedName("age")
val age: String?,
#SerializedName("email")
val email: String?
)
data class UserResponse (
override var status: Int,
override var message: String,
override var timestamp: String,
var data: Data
) : CommonResponse() {
data class Data(
var username: String?,
var token: String?
)
}
my viewModel ( rx )
addDisposable(
model.loginBody(loginRequest)
.subscribeOn(Schedulers.io())
.subscribe({
_loginResult.postValue(it)
}, {
Timber.d("response error, message : ${it.localizedMessage}")
})
)
My current situation is as follows. I need the headers returned by the server after login,
I can see it from the okhttp log, but I don't know how to get a specific header
Word of advice rather than a solution
Not a question of solving your problem but even if it's late, as you're
using Kotlin, a better solution would be to migrate from rxJava to Kotlin Flow. It allows you to use suspend functions to do your retrofit calls, then use Kotlin Flow to do the rxJava's job on the IO thread.
It also allows you to use the Response<T> retrofit object in a simpler way.
Example:
The retrofit request
#POST(YOUR_ROAD) // Retrofit road
suspend fun connect(
#Body credentials: CredentialsObjectBody
): Response<ConnectionObjectResponse>
The repository call to the retrofit request
// The function is suspending the thread with suspend keyword
suspend fun yourFunction(): Flow<DataState<TypeForEmitter>> = flow {
try {
body.clientId = sessionPrefs.deviceName!!
val connectionResponse =
majorApi.connect(
// what you need to put in the body
)
.apply {
if (isSuccessful) {
body()?.let {
// Here you can use what's in the request body
emit(DataState.Success(it))
} ?: throw Exception("Request body was null")
} else {
throw Exception(headers()["message"]) // use throw to handle the error in the catch
}
}
} catch (e: Exception) {
Log.e(TAG, "error: ${e.message}")
emit(DataState.Error(e))
}
}
DataState:
DataState is a sealed class that allows to differentiate emitted status
sealed class DataState<out R> {
data class Success<out T>(val data: T) : DataState<T>()
data class Error(val exception: Exception) : DataState<Nothing>()
object Loading : DataState<Nothing>()
}
How to call the Kotlin flow to launch it on IO thread to prevent blocking the Main (or UI) thread
withContext(Dispatchers.IO) {
yourFunction().onEach {
/*
onEach is like onNext, but emits every type of error, compared to rxJava
that differentiates next/success, complete and error events
*/
}
}
To retreive response headers and other usefull information you can use the Response type from retrofit2 package. To use this change the return type of your login method to Single<retrofit2.Response<UserResponse>>
#POST("user")
fun login( #Body loginRequest : LoginRequest): Single<retrofit2.Response<UserResponse>>
Now to retrieve headers in your ViewModel
addDisposable(
model.loginBody(loginRequest)
.subscribeOn(Schedulers.io())
.subscribe({
val headers = it.headers() // do something with headers
val data = it.body()
_loginResult.postValue(data)
}, {
Timber.d("response error, message : ${it.localizedMessage}")
})
)

Retrofit and Kotlin Post Request 400 error

I am trying to make a simple post request to googles dialogflow in retrofit using kotlin. I am modeling my code off of this site. However, I keep getting 400 errors when trying to make a search so there must be something wrong with my interface creating the message body I believe. I have working python code that does the same functionality as shown here:
url = "https://api.dialogflow.com/v1/query?v=20170712"
headers = {
'Authorization': 'Bearer ' + my_key ,
'Content-Type' : 'application/json'
}
body = {
'lang': 'en',
'query': 'id like to fix my wire c1000 stocks',
'sessionId': 'me'
}
resp = r.post(url,headers=headers,data=json.dumps(body))
I have set this up in android studio as 3 classes:
1) Message.kt
The body of the post request
object Message {
data class MsgBody(val lang: String, val query: String, val sesId: String)
}
2) Model.kt
The response from dialogflow
object Model {
data class Response(val resp: Result)
data class Result(val fulfillment: Fulfillment)
data class Fulfillment(val speech: String)
}
3) DialogFlowService.kt
The interface that has the post request enpoint
interface DialogFlowService {
#Headers(
"Authorization: Bearer {MY API KEY}",
"Content-Type: application/json"
)
#POST("query")
fun getAiMessage(#Body msg: Message.MsgBody,
#Query("v") v: String): Observable<Model1.Response>
companion object {
fun create(): DialogFlowService {
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.dialogflow.com/v1/")
.build()
return retrofit.create(DialogFlowService::class.java)
}
}
}
All of this is then used in my main activity as seen below:
class MainActivity : AppCompatActivity() {
private var disposable: Disposable? = null
private val dialogFlowService by lazy {
DialogFlowService.create()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
if (editText.text.toString().isNotEmpty()) {
sendMessage(editText.text.toString())
}
}
}
private fun sendMessage(msg: String){
disposable = dialogFlowService.getAiMessage(Message.MsgBody("en",msg,"me"),"20170712")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> textView.text = "DialogFlow says: ${result.resp.fulfillment.speech}" },
{ error -> Toast.makeText(this, error.message, Toast.LENGTH_SHORT).show() }
)
}
override fun onPause() {
super.onPause()
disposable?.dispose()
}
}
I tried to follow the tutorial as close as possible and am very confused as to what I did wrong. Like I said above I think this is related to my DialogFlowService.kt file. Thanks for any help in advance.
Error in post request since Model variable name sesId did not equal the actual key sessionId. As Raghunandan said a logging interceptor is very useful

How to get response from server after posting data using RxJava

I am trying to post data on server using retrofit2 and rxjava2 after data posted successfully on server I want to get response from server.I am using kotlin so how can I get server response in my app.
This is what I have done so far:
AddHero.kt
class AddHero : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_hero)
addHero.setOnClickListener {
if(hero.text.toString().equals("")){
Toast.makeText(applicationContext,"Enter superhero name",Toast.LENGTH_SHORT).show()
}
else if(movie.text.toString().equals("")){
Toast.makeText(applicationContext,"Enter movie name",Toast.LENGTH_SHORT).show()
}
else{
saveData()
}
}
}
private fun saveData() {
RetrofitClient.create().saveHero(hero.text.toString(),movie.text.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
}
}
RetrofitClient.kt
object RetrofitClient {
fun create():ApiService{
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(12,TimeUnit.SECONDS)
.readTimeout(12,TimeUnit.SECONDS)
.writeTimeout(12,TimeUnit.SECONDS)
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://www.example.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
.client(okHttpClient)
.build()
val service = retrofit.create(ApiService::class.java)
return service
}
}
ApiService.kt
interface ApiService {
#POST("createHero")
#FormUrlEncoded
fun saveHero(#Field("name") name:String,
#Field("movie") movie:String):Observable<Hero>
}
Hero.kt
data class Hero (
#SerializedName("name")
val name:String,
#SerializedName("movie")
val movie:String
)
Someone please let me know what I am doing wrong or missing. Any help would be appreciated.
THANKS
Your ApiService saveHero function returns Observable<Hero>, you should get your response in subscribe(onNext, onError), like this:
RetrofitClient.create().saveHero(hero.text.toString(), movie.text.toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ hero -> Log.d("AddHeroTag", hero.toString()) },
{ error -> Log.e("AddHero", error.message, error) })
And don't forget to check if your object is non-null

Categories

Resources