This is the response.body I'm receiving
D/OkHttp: <-- 201 Created http://192.168.0.2:8000/api/surveys (962ms)
D/OkHttp: Host: 192.168.0.2:8000
D/OkHttp: Date: Thu, 23 Apr 2020 15:25:40 GMT
D/OkHttp: Connection: close
D/OkHttp: X-Powered-By: PHP/7.3.10
D/OkHttp: Cache-Control: no-cache, private
D/OkHttp: Date: Thu, 23 Apr 2020 15:25:40 GMT
D/OkHttp: Content-Type: application/json
D/OkHttp: X-RateLimit-Limit: 60
D/OkHttp: X-RateLimit-Remaining: 59
//this is what I want to save
D/OkHttp: [{"id":2,"evaluation_id":1,"user_id":null,"group_id":1,"created_at":"2020-04-20 11:04:31",
"updated_at":"2020-04-20 11:04:31","answered":false,"evaluation":{"id":1,"survey_id":1,
"init_date":"2020-04-13 12:00:00","end_date":"2020-04-30 12:00:00","report":0,"report_date":null,
"report_end":null,"created_at":"2020-04-20 11:04:21","updated_at":"2020-04-20 11:04:21",
"survey":{"id":1,"name":"asdfasdfds group","description":"<p>adfadsfa<strong><em>dsf<\/em>
<\/strong>asdfadsf asd asdf f sad fsd<\/p>","user_id":1,"anonymous":0,"created_at":"2020-04-20 11:04:21",
"updated_at":"2020-04-20 11:04:31"}}},{"id":5,"evaluation_id":2,"user_id":1,"group_id":null,
"created_at":"2020-04-21 11:00:19","updated_at":"2020-04-21 11:00:19","answered":true,
"evaluation":{"id":2,"survey_id":2,"init_date":"2020-04-13 12:00:00","end_date":"2020-04-24 12:00:00","report":0,
"report_date":null,"report_end":null,"created_at":"2020-04-20 11:05:43","updated_at":"2020-04-20 11:05:43",
"survey":{"id":2,"name":"asdfa single","description":"<p>asdfa<\/p>","user_id":1,"anonymous":1,
"created_at":"2020-04-20 11:05:43","updated_at":"2020-04-21 11:00:19"}}}]
D/OkHttp: <-- END HTTP (1123-byte body)
I have created the POJO models for the objects inside the main object, this is a retrofit response so I have it like this for now
#Override
public void onResponse(Call<Survey> call, Response<Survey> response) {
if (!response.isSuccessful()) {
FancyToast.makeText(activity, getString(R.string.error),
FancyToast.LENGTH_SHORT, FancyToast.CONFUSING,
R.drawable.error_outline, false).show();
return;
}
System.out.println("response: " + response.body());
}
How do I get the JSON from the response body to then save in the models?
Here is how your model class should be:
class Model : ArrayList<ModelItem>(){
data class ModelItem(
val answered: Boolean = false,
val created_at: String = "",
val evaluation: Evaluation = Evaluation(),
val evaluation_id: Int = 0,
val group_id: Int = 0,
val id: Int = 0,
val updated_at: String = "",
val user_id: Int = 0
) {
data class Evaluation(
val created_at: String = "",
val end_date: String = "",
val id: Int = 0,
val init_date: String = "",
val report: Int = 0,
val report_date: Any = Any(),
val report_end: Any = Any(),
val survey: Survey = Survey(),
val survey_id: Int = 0,
val updated_at: String = ""
) {
data class Survey(
val anonymous: Int = 0,
val created_at: String = "",
val description: String = "",
val id: Int = 0,
val name: String = "",
val updated_at: String = "",
val user_id: Int = 0
)
}
}
}
Make sure your are using GSON converter library in your retrofit.
Related
I would like to upload image to api. I have headers for request and also headers for Part with files:
#Multipart
#POST("/api")
suspend fun uploadFile(
#Part("payload") file: RequestBody,
#Header("Mobile-Client-Type") mobileClientType: String,
#Header("Mobile-Client-Version") version: String,
#Header("Authorization") authorization: String,
): retrofit2.Response<Any>
and
internal class RetrofitFileUpload {
private val fileUploadService = FileUploadService.create()
suspend fun requestWithPayload(
urlString: String,
file: File,
myHeaders:
pl.probs.uploadapp.jsonObjects.Headers,
payload: Payload
): retrofit2.Response<Any> {
val mediaType = "multipart/form-data".toMediaTypeOrNull()
val fileBody = RequestBody.create(mediaType, file)
val body: RequestBody = MultipartBody.Builder().addPart(
Headers.headersOf(
"type",
payload.type,
"identifier",
payload.identifier,
"omit_package_creation",
payload.omitPackageCreation.toString(),
"file_path",
payload.filePath.toString()
), fileBody
).build()
return fileUploadService.uploadFile(
body,
myHeaders.mobileClientType,
myHeaders.mobileClientVersion,
myHeaders.authorization,
)
}
}
Result looks like I have 2 parts instead of one:
Content-Type: multipart/form-data; boundary=eac4d249-196d-421e-9ffe-923b472a5f58
....other headers...........
--eac4d249-196d-421e-9ffe-923b472a5f58
Content-Disposition: form-data; name="payload"
Content-Transfer-Encoding: binary
Content-Type: multipart/mixed; boundary=55d4b971-d171-42a7-a82a-5deb1bccac98
Content-Length: 2421
--55d4b971-d171-42a7-a82a-5deb1bccac98
type: myType
identifier: myID
omit_package_creation: true
file_path: /data/0...
Content-Type: multipart/form-data
Content-Length: 2072
{[Content from file]}
--55d4b971-d171-42a7-a82a-5deb1bccac98--
WHat should I change to have only 1 part?
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)
}
}
I want to get altitude from api service and:
base url is https://api.open-elevation.com/
end point is api/v1/lookup?
and get two args lat and long like this
https://api.open-elevation.com/api/v1/lookup?locations=42,35
output is a json
How can i send get request and get data and please help
Hm. unfortunately you can't send a request with varargs through retrofit. But, you can do it like this.
Do something like this..
lifecycleScope.launch {
val latLong = arrayOf(41.161758, -8.583933)
val result = withContext(Dispatchers.IO) {
RetroClient.getInstance().lookup(latLong.joinToString())
}.awaitResponse()
if (result.isSuccessful && result.code() == 200) {
// If success do your sutff here
}
}
RetroClient
object RetroClient {
private val httpInterceptor by lazy {
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
}
private val client by lazy {
OkHttpClient.Builder()
.addInterceptor(httpInterceptor)
.callTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.build()
}
private val gson by lazy {
GsonBuilder().setLenient().create()
}
fun getInstance(): RetroInterface {
return Retrofit.Builder()
.baseUrl("https://api.open-elevation.com/api/v1/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson)).build()
.create(RetroInterface::class.java)
}
}
RetroInterface
interface RetroInterface {
#GET("lookup")
fun lookup(
#Query("locations") locations: String,
): Call<LocationsModel?>
}
LocationsModel
data class LocationsModel(
var results: List<Result>?
)
data class Result(
var elevation: Int?,
var latitude: Double?,
var longitude: Double?
)
RESULTS
I/okhttp.OkHttpClient: <-- 200 OK https://api.open-elevation.com/api/v1/lookup?locations=41.161758%2C%20-8.583933 (1411ms)
I/okhttp.OkHttpClient: Server: nginx/1.21.1
I/okhttp.OkHttpClient: Date: Sat, 27 Aug 2022 18:33:55 GMT
I/okhttp.OkHttpClient: Content-Type: application/json
I/okhttp.OkHttpClient: Transfer-Encoding: chunked
I/okhttp.OkHttpClient: Connection: keep-alive
I/okhttp.OkHttpClient: Access-Control-Allow-Origin: *
I/okhttp.OkHttpClient: Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS
I/okhttp.OkHttpClient: Access-Control-Allow-Headers: Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token
I/okhttp.OkHttpClient: Strict-Transport-Security: max-age=31536000; includeSubDomains
I/okhttp.OkHttpClient: {"results": [{"latitude": 41.161758, "longitude": -8.583933, "elevation": 117}]}
I/okhttp.OkHttpClient: <-- END HTTP (80-byte body)
Hope you understand. - Thank you.
I am using retrofit2 for API calls. When it called to my API it shows below error;
Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
#Step1: Here is my JONSON
[
{
"branchCode": "053",
"logitude": " ",
"phone": " +8809613225301",
"latitude": " ",
"sl": "1",
"branchName": "A K KHAN MOOR BRANCH",
"branchAddress": "The Downing(1st Floor), H-825,, Zakir Hossain Road, A K Khan,",
"opendate": " ",
"fax": " n/a"
},
{
"branchCode": "058",
"logitude": " ",
"phone": " ",
"latitude": " ",
"sl": "2",
"branchName": "AGANAGAR BRANCH",
"branchAddress": "Aganagar Branch, Shawon Plaza (1st Floor)",
"opendate": " ",
"fax": " "
}
]
#2Step: here is my data class
data class LocationdataModel(
#SerializedName("sl")
var sl: String = "",
#SerializedName("atmCode")
var atmCode: String = "",
#SerializedName("atmLocation")
var atmLocation: String = "",
#SerializedName("atmLogitude")
var atmLogitude: String = "",
#SerializedName("atmLatitued")
var atmLatitued: String = "",
#SerializedName("branchCode")
var branchCode: String = "",
#SerializedName("branchName")
var branchName: String = "",
#SerializedName("branchAddress")
var branchAddress: String = "",
#SerializedName("phone")
var phone: String = "",
#SerializedName("fax")
var fax: String = "",
#SerializedName("logitude")
var logitude: String = "",
#SerializedName("latitude")
var latitude: String = "",
#SerializedName("opendate")
var opendate: String = "",
#SerializedName("atmName")
var atmName: String = ""
)
#Step3: here is my API
#GET("api/location_api")
fun getBranchLocation(
#Query("requestCode") requestCode: String?
): Single<ArrayList<LocationdataModel>>
#Step4: My API Server from where I called my api
var okHttpClient: OkHttpClient? = OkHttpClient.Builder()
.connectTimeout(50, TimeUnit.SECONDS)
.readTimeout(50, TimeUnit.SECONDS)
.writeTimeout(50, TimeUnit.SECONDS)
.callTimeout(50, TimeUnit.SECONDS)
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.header("Authorization", "myauth")
val request = requestBuilder.build()
chain.proceed(request)
}
.build()
private val api = Retrofit.Builder()
.baseUrl(baseurl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
.create(Api::class.java)
fun getBranchLocation(model: LocationModel): Single<ArrayList<LocationdataModel>> {
return api.getBranchLocation(
model.requestCode
)
}
#Step5: here is my ViewModel
fun getBranchLocation(model: LocationModel){
disposable.add(apiService.getBranchLocation(model)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<List<LocationdataModel>>() {
override fun onSuccess(model: List<LocationdataModel>) {
model.let {
getBranchLocationonList_response.value = model
}
}
override fun onError(e: Throwable) {
Log.e("error-->", e.message.toString())
e.printStackTrace()
}
})
Whate is the wrong with my code? Please help me
I am trying to call a post API with Retrofit using coroutine but after hours of searching I am unable to find the mistake. There is no clear information about the error.
Below is the simple JSON I should be getting after posting the data.
{
"status": "OK",
"data": 5,
"message": null,
"code": "200"
}
api call from fragment
lifecycleScope.launchWhenStarted {
sharedPreferences.getString(AppConstant.token, "")?.let { authToken ->
viewModelSubProperty.sendDetail(authToken,propertyData)
}
}
where propertyData is the values I am send to the api.
ViewModel
suspend fun sendDetail(token: String,data: SubPropertyData) {
viewModelScope.launch(Dispatchers.IO) {
val responseResult = repository.sendDetail(token = token,data)
withContext(Dispatchers.Main) {
if(responseResult is Result.Success) {
Log.e("Viewmodel", "${responseResult.data}")
Log.e("Viewmodel", "$responseResult")
}
else if (responseResult is Result.Error && responseResult.exception.message == AppConstant.INTERNET_ERR_MSG)
EventBus.getDefault().post(WebServiceErrorEvent(null, true))
else if (responseResult is Result.Error)
EventBus.getDefault().post(WebServiceErrorEvent(responseResult))
}
}
}
DataSource
suspend fun sendDetail(token: String, data: SubPropertyData): Result<AddUpdateDataResponse> {
val serverResponse: Response<AddUpdateDataResponse>
return try {
serverResponse = serverApi.sendDetail(token,data)
if(serverResponse.isSuccessful)
Result.Success(serverResponse.body()!!)
else {
Log.e("what-code ", ""+serverResponse.errorBody())
Log.e("what-code ", serverResponse.errorBody()?.charStream().toString())
Result.Success(serverResponse.body()!!)
}
} catch (e: Throwable) {
if(e is NoConnectivityException)
Result.Error(IOException(AppConstant.INTERNET_ERR_MSG))
else
Result.Error(IOException(e.localizedMessage))
}
}
Api Call
#POST("api/addOrUpdateSubjectPropertyDetail")
suspend fun sendDetail(
#Header("Authorization") Authorization:String,
#Body data: SubPropertyData
) : Response<AddUpdateDataResponse>
Pojo class
data class AddUpdateDataResponse(
#SerializedName("code")
val code: String?,
#SerializedName("data")
val data: Int?,
#SerializedName("message")
val message: String? = null,
#SerializedName("status")
val status: String?
)
Logs
I/okhttp.OkHttpClient: --> POST https://abc/api/AddOrUpdateSubjectPropertyDetail
Content-Type: application/json; charset=UTF-8
Content-Length: 398
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiI0IiwiaHR0cDovL3NjaGVtYXnzK5gv0k4
I/okhttp.OkHttpClient: {"address":{"city":"Karachi","countryId":1,"countryName":"Pak","countyId":1,"countyName ":"SSS","stateId":11,"stateName":"Sindh","street":"akl","unit":"00","zipCode":"123"},"appraisedPropertyValue":100000.0,"floodInsurance":0.0,"homeOwnerInsurance":200.0,"isMixedUseProperty":true,"loanApplicationId":5,"mixedUsePropertyExplanation":"ashh","occupancyTypeId":1,"propertyTax":100.0,"propertyTypeId":1}
--> END POST (398-byte body)
--> END POST (398-byte body)
I/okhttp.OkHttpClient: <-- 500
https://abc/api/AddOrUpdateSubjectPropertyDetail (167ms)
I/okhttp.OkHttpClient: server: Microsoft-IIS/10.0
I/okhttp.OkHttpClient: x-powered-by: ASP.NET
date: Mon, 08 Nov 2021 22:31:31 GMT
I/okhttp.OkHttpClient: <-- END HTTP (0-byte body)
E/what-code: null
null
okhttp3.ResponseBody$BomAwareReader#3554188