I am trying to send an array of json objects using retrofit, while this is working with raw JSON in postman I am having trouble in Android. Right now I am just doing one value in the array, it will do more later.
Api Interface
#Headers({
"Content-Type:application/json"
})
#PUT("/People")
Call<Task> updatePeople(
#retrofit2.http.Header("Authorization") String authorization, #retrofit2.http.Body List<Person> body
);
request
fun updatePeople(person: Person, legId:Int){
val peopleList = listOf(person)
personApi.updatePeople(token, peopleList).enqueue(object : Callback<Task>{
override fun onFailure(call: Call<List<Person>>, t: Throwable)
{
//Error
}
override fun onResponse(call: Call<List<Person>>, response: Response<List<Person>>)
{
if(response.code() == 200)
{
//It works
}
}
})
}
Try to add "hasBody = true" like this:
#FormUrlEncoded
#Headers("Content-Type: application/json")
#HTTP(method = "PUT", path = "/People", hasBody = true)
Call<Task> updatePeople(#retrofit2.http.Header("Authorization") String authorization, #retrofit2.http.Body List<Person> body);
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?
So I'm still in the process of learning android dev and I'm currently working on an app which is supposed to show students their grades. Right now I am stuck at getting login to a service from which grades are collected. For that process I am using https://eduo-ocjene-docs.vercel.app/ api (documentation is in Croatian).
This is what curl request for logging in looks like:
curl --location --request GET 'https://ocjene.eduo.help/api/login' \--header 'Content-Type: application/json' \--data-raw '{ "username":"ivan.horvat#skole.hr", "password":"ivanovPassword123"}'
Here are screenshots of what I have tried until now
Here is how I build retrofit
object ApiModule {
private const val BASE_URL = "https://ocjene.eduo.help/"
lateinit var retrofit: EdnevnikApiService
private val json = Json { ignoreUnknownKeys = true }
fun initRetrofit() {
val okhttp = OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}).build()
retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.client(okhttp).build().create(EdnevnikApiService::class.java)
}
}
The login method
interface EdnevnikApiService {
#HTTP(method = "get", path = "/api/login", hasBody = true)
fun login(#Body request: LoginRequest): Call<LoginResponse>
}
This is what happens when the login button is clicked
fun onLoginButtonClicked(email: String, password: String) {
val request = LoginRequest(email, password)
ApiModule.retrofit.login(request).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
loginResultLiveData.value = response.isSuccessful
val body = response.body()
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
loginResultLiveData.value = false
}
})
}
and this is what kotlin request and kotlin response data classes look like
#kotlinx.serialization.Serializable
data class LoginRequest(
#SerialName("username") val username: String,
#SerialName("password") val password: String,
)
#kotlinx.serialization.Serializable
data class LoginResponse(
#SerialName("LoginSuccessful") val isSuccessful: Boolean,
)
Oh and this is what I get from the interceptor when I send the request
My guess is server is responding with 400 Bad Request due to unsupported method type. When I replaced method = "get" with method = "GET" in your sample code, I received:
java.lang.IllegalArgumentException: method GET must not have a request body.
which makes sense. Luckily, the /login API you shared works with POST method type, so you can try using:
#HTTP(method = "POST", path = "/api/login", hasBody = true,)
I checked at my end and I received the following response:
<-- 200 https://ocjene.eduo.help/api/login (1390ms)
access-control-allow-origin: *
access-control-allow-credentials: true
set-cookie: epicCookie=f69fbd6d4f10b5cc38e038b5da0843b356776c58c4fb32aed24dbcc49026778724bc25e21448c05a29df9f4b5558b254011fb3f8a992710f9901f23c53be5eaadaa799f3f5ac9e18de191bed02ef3e96030b83042ee8392755b03dd785edca6a;
content-type: application/json; charset=utf-8
etag: "bkrbkvg0eo6c"
vary: Accept-Encoding
date: Thu, 10 Nov 2022 03:07:08 GMT
server: Fly/b1863e2e7 (2022-11-09)
via: 2 fly.io
fly-request-id: 01GHFR2T56X9K0GFN3DH1Z9JYV-sin
{"LoginSuccessful":false,"token":"f69fbd6d4f10b5cc38e038b5da0843b356776c58c4fb32aed24dbcc49026778724bc25e21448c05a29df9f4b5558b254011fb3f8a992710f9901f23c53be5eaadaa799f3f5ac9e18de191bed02ef3e96030b83042ee8392755b03dd785edca6a"}
<-- END HTTP (228-byte body)
object ApiModule {
private const val BASE_URL = "https://ocjene.eduo.help/"
lateinit var retrofit: EdnevnikApiService
private val json = Json { ignoreUnknownKeys = true }
fun initRetrofit() {
val okhttp = OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}).build()
retrofit = Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.client(okhttp).build().create(EdnevnikApiService::class.java)
}
}
val url = URL("https://securegw.paytm.in/theia/api/v1/initiateTransaction?mid=$merchantId&orderId=$orderId")
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json")
connection.doOutput = true
val requestWriter = DataOutputStream(connection.outputStream)
val postData = AnyJSONObject.toString()
requestWriter.writeBytes(postData)
requestWriter.close()
val responseReader = BufferedReader(InputStreamReader(connection.inputStream))
responseReader.readLine()?.let { responseData->
withContext(Dispatchers.Main){
onResponse(responseData)
}
}
responseReader.close()
Above Code is working Fine I just want to convert it to RestApi Call
This is how I have tried
#Headers("Accept: application/json")
#POST("initiateTransaction")
#FormUrlEncoded
suspend fun getCheckSum(
#Query("mid") mid: String,
#Query("orderId") orderId: String,
#Field("extraParamsMap") jsonObjectAsString: String
): JsonObject
I am not sure that Field is right or what. I have just tried to do it this way.
I don't have much exp with retrofit
I write one example for GET/POST method.
#GET("new_market/service.php?action=getsidebar&packageName=com.gameloft.android.ANMP")
Call<ResultData> doReadyRequest();
#POST("stats_mafia_city")
Call<ResultData> doSendClanData(#Header ("bot_secret") String bot_secret, #Header ("Content-Type") String content_type, #Body JsonObject jsonObject);
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()
}
}
Following is my request parameters in PostMan
{"assign_id":"1","type":2,"attendance_list":[{"stud_id":"1703","attendanceID":"1","stud_attendance":"4"},{"stud_id":"1704","attendanceID":"2","stud_attendance":"1"},{"stud_id":"1705","attendanceID":"3","stud_attendance":"1"},{"stud_id":"1706","attendanceID":"4","stud_attendance":"1"},{"stud_id":"1707","attendanceID":"5","stud_attendance":"1"},{"stud_id":"1727","attendanceID":"25","stud_attendance":"1"}]}
Following is the response
{"status":1,"msg":"Success"}
Now in my Android App I am using Retrofit with Gson. But passing through Gson, I was facing some problem so I am sending request parameters in form of jsonObject and jsonArrays.
Following is my code when a button is pressed to submit request to server
val jObjRequest = JsonObject()
jObjRequest.addProperty("assign_id",ClassModelInstance.getInstance().classInfo.assignId)
jObjRequest.addProperty("type","2")
val attendanceArray = JsonArray()
for(i in 0 until ClassModelInstance.getInstance().studentInfos.size){
val jsonObject = JsonObject()
jsonObject.addProperty("stud_id",ClassModelInstance.getInstance().studentInfos[i].studId)
jsonObject.addProperty("attendanceID",1)
jsonObject.addProperty("stud_attendance",ClassModelInstance.getInstance().studentInfos[i].studAttendance)
attendanceArray.add(jsonObject)
}
jObjRequest.addProperty("attendance_list",attendanceArray.toString())
Log.i("PritishAttendanceApi2", jObjRequest.toString())
val submitAttendanceInterface = ApiClient.client.create(SubmitAttendanceInterface::class.java)
submitAttendanceInterface.takeAttendance(jObjRequest)
.enqueue(object : Callback<SubmitAttendanceResponse> {
override fun onFailure(call: Call<SubmitAttendanceResponse>, t: Throwable) {
activity?.let { it1 -> ToastMaker.make(it1,getString(R.string.something_went_wrong),Toast.LENGTH_LONG) }
Log.i("Pritish",t.message+"\t"+t.localizedMessage+"\t"+t.printStackTrace()+"\t"+t.cause+"\n"+call.request())
alertDialog.dismiss()
}
override fun onResponse(call: Call<SubmitAttendanceResponse>, response: Response<SubmitAttendanceResponse>) {
if(response.body()?.status.toString().equals("1",true)){
activity?.let { it1 -> ToastMaker.make(it1,response.body()?.msg.toString(),Toast.LENGTH_LONG) }
goToPreviousFragment()
} else {
activity?.let { it1 -> ToastMaker.make(it1,response.body()?.msg.toString(),Toast.LENGTH_LONG) }
}
alertDialog.dismiss()
}
})
This is the interface and response class
interface SubmitAttendanceInterface {
#Headers("Content-Type: application/json")
#POST("timetable/takeAttendance")
fun takeAttendance(#Body body: JsonObject): Call<SubmitAttendanceResponse>
}
data class SubmitAttendanceResponse(
#SerializedName("status")
#Expose
var status: Int? = null,
#SerializedName("msg")
#Expose
var msg: String? = null
)
When I log using HttpInterceptor I get com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 1 path
I searched Stack Overflow for the above error but the answers didn't met my requirement
JSON Error "java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"
"Expected BEGIN_OBJECT but was STRING at line 1 column 1"
I have edited the url in the logs as I don't want to expose the URL.
as per your log and sample data you should post data "attendance_list" as an json array insted of string
try
jObjRequest.add("attendance_list",attendanceArray)
insted of
jObjRequest.addProperty("attendance_list",attendanceArray.toString())