I looking for a way to send a zip file for a server using retrofit in kotlin. Some functions are deprecated that's make this more dificulte. When I star the request the response is NULL, there is no error.
That is the function who start the request:
fun sendZipeFile() {
val retrofitClient = NetworkUtils
.getRetrofitInstance("http://url.com")
val sendZipeFileEndPoint = retrofitClient.create(SendZipeFile::class.java)
var file = File("/storage/emulated/0/Download/file.zip")
val requestImageFile = file?.asRequestBody("multipart/form-data".toMediaType())
val callback = startZipeFileUpload.zipeFileUpload("application/zip","Atualização", requestImageFile, "0.0.2")
callback.enqueue(object : Callback<UploadZipFile> {
override fun onFailure(call: Call<UploadZipFile>, t: Throwable) {
t.message
t.fillInStackTrace()
}
override fun onResponse(call: Call<UploadZipFile>, response: Response<UploadZipFile>) {
val responseString = response.body()
}
})
}
Below is an interface
interface SendZipeFile{
#Headers("Content-Type: multipart/form-data")
#Multipart
#POST("updateModelsAndParameters/" +
"")
fun zipeFileUpload(
#Header("Content-Type") contentType: String?,
#Part("description") description: String?,
#Part("Zip") file: RequestBody?,
#Part("flag")flag: String?,
): Call<UploadZipFile>
}
And the data class
import com.google.gson.annotations.SerializedName
data class UploadZipFile(
#SerializedName("status_code") var statusCode: Int? = null,
#SerializedName("message") var message: String? = null,
#SerializedName("elapsed_ms") var elapsedMs: Double? = null,
#SerializedName("response") var resposta: Boolean? = null,
)
Related
So I'm trying to use themoviedb for extracting search results for movies. The url is as follows:
https://api.themoviedb.org/3/search/movie?api_key={apikey}&language=en-US&query={query}
Where in the query I insert the keyword that I want to search. I'm using retrofit library to do this.
This is my code for my ApiService:
interface ApiService {
#GET("3/search/movie?api_key=${BuildConfig.MOVIE_TOKEN}&language=en-US&")
fun getMovies(
#Query("query") query: String
): Call<SearchMovieResponse>
}
This is my code for the ApiConfig object:
class ApiConfig {
companion object {
fun getApiService(): ApiService{
val loggingInterceptor =
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.themoviedb.org/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
return retrofit.create(ApiService::class.java)
}
}
}
I also have a RemoteDataSouce class which uses that config to get the movies. I have also generated the data class to using POJO. This is the method in the RemoteDataSource class that uses that the API config.
fun getMovies():List<MoviesItem>?{
val client = ApiConfig.getApiService().getMovies("john")
var listMovies: ArrayList<MoviesItem> = ArrayList<MoviesItem>()
client.enqueue(object: Callback<SearchMovieResponse> {
override fun onResponse(call: Call<SearchMovieResponse>, response: Response<SearchMovieResponse>) {
if (response.isSuccessful){
val rawList = response.body()?.results!!
for (item in rawList){
listMovies.add(item)
}
}
}
override fun onFailure(call: Call<SearchMovieResponse>, t: Throwable) {
return
}
})
return listMovies
}
The json response of the API is this:
The data model that I use for SearchMovieResponse is this:
data class SearchShowResponse(
#field:SerializedName("page")
val page: Int? = null,
#field:SerializedName("total_pages")
val totalPages: Int? = null,
#field:SerializedName("results")
val results: List<ShowsItem?>? = null,
#field:SerializedName("total_results")
val totalResults: Int? = null
)
data class ShowsItem(
#field:SerializedName("first_air_date")
val firstAirDate: String? = null,
#field:SerializedName("overview")
val overview: String? = null,
#field:SerializedName("original_language")
val originalLanguage: String? = null,
#field:SerializedName("genre_ids")
val genreIds: List<Int?>? = null,
#field:SerializedName("poster_path")
val posterPath: String? = null,
#field:SerializedName("origin_country")
val originCountry: List<String?>? = null,
#field:SerializedName("backdrop_path")
val backdropPath: String? = null,
#field:SerializedName("original_name")
val originalName: String? = null,
#field:SerializedName("popularity")
val popularity: Double? = null,
#field:SerializedName("vote_average")
val voteAverage: Double? = null,
#field:SerializedName("name")
val name: String? = null,
#field:SerializedName("id")
val id: Int? = null,
#field:SerializedName("vote_count")
val voteCount: Int? = null
)
However, the listMovies is returning null. I'm not sure what I did wrong here. Can anyone explain? Thanks
Your method getMovies() is returning the list before the Retrofit call is done, you are using enqueue() method that run it asynchronous so your method finish before the onResponse() method is called.
Solution, rewrite your code thinking about this information or use execute()method instead enqueue(), this will execute the call in the main thread so you will have to call it in a new thread or a coroutine.
As, you are using enqueue() that run asynchronous so your function finish before the onResponse() method is called. So you have to return the list after on the complete of the process.
fun getMovies():List<MoviesItem>?{
val client = ApiConfig.getApiService().getMovies("john")
var listMovies: ArrayList<MoviesItem> = ArrayList<MoviesItem>()
client.enqueue(object: Callback<SearchMovieResponse> {
override fun onResponse(call: Call<SearchMovieResponse>, response: Response<SearchMovieResponse>) {
if (response.isSuccessful){
val rawList = response.body()?.results!!
for (item in rawList){
listMovies.add(item)
}
return listMovies
}
}
override fun onFailure(call: Call<SearchMovieResponse>, t: Throwable) {
return
}
})
}
Try to use callback to return your list:
fun getMovies(callback: (List<MoviesItem>) -> Unit) {
val client = ApiConfig.getApiService().getMovies("john")
client.enqueue(object : Callback<SearchMovieResponse> {
override fun onResponse(
call: Call<SearchMovieResponse>,
response: Response<SearchMovieResponse>
) {
var listMovies: ArrayList<MoviesItem> = ArrayList<MoviesItem>()
if (response.isSuccessful) {
val rawList = response.body()?.results!!
for (item in rawList) {
listMovies.add(item)
}
}
callback(listMovies)
}
override fun onFailure(call: Call<SearchMovieResponse>, t: Throwable) {
callback(emptyList()) // or throw error or use Result structure
}
})
}
So I have to handle a POST request with its body data taken from some input in a form.
The Endpoint of this service is https://someUrl.com/switching-product/switch?orderID=A002&procode=0200011&nip=P19120
The Response returned from Postman is like this.
The body of this request is like this :
In this case, I have this Interface for handling a POST request:
///Endpoint: https://someUrl.com/switching-product/switch?orderID=A002&procode=0200011&nip=P19120
interface editProductDetail{
#FormUrlEncoded
#POST("switch")
fun editProductDetail(#Query("orderID") orderID: String,
#Query("procode") procode: String,
#Query("nip") nip : String,
#Field("procode_new") procodeNew: String,
#Field("item_qty_new") itemQtyNew: String,
#Field("item_price_new") itemPriceNew: String,
#Field("item_name_new") itemNameNew: String,
#Field("total_price_new") totalPriceNew: String): Call<OutstandingOrderDetailPOJODataClassDetailItem>
}
This is the data class I use:
data class OutstandingOrderDetailPOJODataClassDetailItem(
#field:SerializedName("item_price_new")
val itemPriceNew: Int? = null,
#field:SerializedName("item_name_new")
val itemNameNew: String? = null,
#field:SerializedName("total_price")
val totalPrice: Int? = null,
#field:SerializedName("item_price")
val itemPrice: Int? = null,
#field:SerializedName("item_name")
val itemName: String? = null,
#field:SerializedName("status_refund")
val statusRefund: String? = null,
#field:SerializedName("detail_id")
val detailId: Int? = null,
#field:SerializedName("procode_new")
val procodeNew: String? = null,
#field:SerializedName("refund_date")
val refundDate: String? = null,
#field:SerializedName("request_refund")
val requestRefund: String? = null,
#field:SerializedName("procode")
val procode: String? = null,
#field:SerializedName("last_update")
val lastUpdate: String? = null,
#field:SerializedName("item_qty_new")
val itemQtyNew: Int? = null,
#field:SerializedName("order_id")
val orderId: String? = null,
#field:SerializedName("total_price_new")
val totalPriceNew: Int? = null,
#field:SerializedName("item_qty")
val itemQty: Int? = null,
#field:SerializedName("refund")
val refund: Int? = null
)
As you can see above, the body has attributes that are included in the data class as well. (This data class is also used in a related service in the App that uses the inputted data of this service).
And this is the function to trigger the POST request:
NetworkConfig().editOutstandingOrderProductDetailService().editProductDetail(
selectedOrderId,
selectedProcode,
selectedNip,
procodeNew,
itemNewQty,
itemPriceNew,
itemNewName,
totalPriceNew
).enqueue(object :
Callback<OutstandingOrderDetailPOJODataClassDetailItem> {
override fun onFailure(call: Call<OutstandingOrderDetailPOJODataClassDetailItem>, t: Throwable) {
Log.i("Order", "It Failed!!")
if (call.isCanceled) {
Toast.makeText((activity as AppCompatActivity), "Request Aborted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText((activity as AppCompatActivity), t.localizedMessage, Toast.LENGTH_SHORT).show()
}
}
override fun onResponse(
call: Call<OutstandingOrderDetailPOJODataClassDetailItem>,
response: Response<OutstandingOrderDetailPOJODataClassDetailItem>
) {
Log.i("Order", "Switching Process done!!!")
Log.i("Order", "Status: ${response.body()}")
}
})
From above, it prints the response in the logCat like this:
Am I missing something here? Or there's something I need to Change? If there's any detail that I missed to point out, just let me know!
Your request is a JSON object, not a formurl.
#Field tag is used when you want to pass your parameters as formurl
Use model class or JsonObject with #Body tag to pass parameters as JsonObject.
Model class Exmaple,
data class TestClass{
val text1: String,
val text2: String
}
Now Pass this class as request
#POST("URL")
fun apiName(#Body request: TestClass);
JSON Object Exmaple,
JSONObject paramObject = new JSONObject();
paramObject.put("key1", "value1");
paramObject.put("key1", "vaalue2");
Now pass it as String or JsonObject
#POST("URL")
fun apiName(#Body request: String); // pass as String
How can I solve this problem?
class InformationActivity : AppCompatActivity() {
private val _tag = SplashActivity::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_information)
val uniqueId = SharedPreference.getidInfo(this)
val token = SharedPreference.getUserInfo(this)
Client.retrofitService.profile(uniqueId, token)
.enqueue(object : Callback<LoginResponse> {
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
}
override fun onResponse(
call: Call<LoginResponse>,
response: Response<LoginResponse>
) {
if (response?.isSuccessful == false) { val er = Gson().fromJson(response.errorBody()?.charStream(), ErrorResponse::class.java)
Log.d(_tag, "${er.code}:${er.message}")
if (er.code == 60203) {
Toast.makeText(this#InformationActivity, "", Toast.LENGTH_SHORT).show()
}
} else if (response?.isSuccessful == true) {
Glide.with(applicationContext).asBitmap().load("https://s3.amazonaws.com/appsdeveloperblog/micky.gif").into(imageView)
Toast.makeText(this#InformationActivity, "", Toast.LENGTH_LONG).show()
val file=File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"micky.gif")
var fileName="micky.gif"
val token = SharedPreference.getUserInfo(applicationContext)
val uniqueId= SharedPreference.getidInfo(applicationContext)
var requestBody: RequestBody = RequestBody.create(MediaType.parse("image/*"), file)
val body: MultipartBody.Part = MultipartBody.Part.createFormData("profile",fileName,requestBody)
if (uniqueId != null) {
Client.retrofitService.updateProfile(token,uniqueId,body)
.enqueue(object : Callback<List<LoginResponse>> {
override fun onFailure(
call: Call<List<LoginResponse>>,
t: Throwable) { Log.d("", t.message) }
override fun onResponse(
call: Call<List<LoginResponse>>,
response: Response<List<LoginResponse>>) { if (response?.isSuccessful)
{ Toast.makeText(this#InformationActivity, "File Uploaded Successfully...", Toast.LENGTH_LONG).show()
Log.d("", "" + response?.body().toString())
} else {
Toast.makeText(this#InformationActivity, "Some error occurred...", Toast.LENGTH_LONG).show()
}
} }) }
}
}
}) }
}
interface API {
#Headers("Content-Type: application/json", "Authorization:token:String")
#Multipart
#PUT("/user/profile/{userId}")
fun updateProfile(#Header("Authorization") token: String?, #Path("userId") userID: String, #Part file: MultipartBody.Part): Call<List<Loginresponse>>
}
object Client {
var retrofitService: API
init {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val logger: OkHttpClient = OkHttpClient.Builder().addInterceptor(interceptor).readTimeout(20, TimeUnit.SECONDS).writeTimeout(20, TimeUnit.SECONDS).build()
val retrofit = Retrofit.Builder()
.baseUrl("myurl")
.addConverterFactory(GsonConverterFactory.create())
.client(logger)
.build()
retrofitService = retrofit.create(API::class.java)
}
}
#SerializedName("uniqueId")
val user:String?=null
#SerializedName("nickname")
val nickname: String?=null
#SerializedName("birth")
val birth: String?=null
#SerializedName("profileImage")
val profileImage: String?=null
#SerializedName("profileThumbnail")
val profileThumbnails: String?=null
#SerializedName("gender")
val gender: Int?=null
#SerializedName("token")
val token: String? = null
}
Your json return as JSON object. But you are trying to convert into Json array
Call<List<Loginresponse>> - you try to convert result as JSON Array (list)
Solution
Get the raw json result & convert the pojo using http://www.jsonschema2pojo.org/ and try again
You are trying to store json object in list that's why you are getting error .
check your JSON response start with { curly bracket it means it is object not an array . array start with [ square bracket .
#PUT("/user/profile/{userId}")
fun updateProfile(#Header("Authorization") token: String?, #Path("userId") userID: String, #Part file: MultipartBody.Part): Call<List<Loginresponse>>
replace Call<List<Loginresponse>> with Call<Loginresponse> all over where you using updateProfile method
I can not find a solution to this problem on the Internet. Or is my code so bad?
Interface
interface GetWeatherService {
#GET("/data/2.5/forecast?")
fun getAllWeather(#Query("q")cityName: String, #Query("APPID")app_id: String, #Query("units")units: String="imperial"): Call<ListWeather>
#GET("/data/2.5/weather?")
fun getCurrentWeather(#Query("q")cityName: String, #Query("APPID")app_id: String, #Query("units")units: String="imperial"): Call<MainWeather>
#GET("/data/2.5/weather?")
fun getWeatherDataFromLocation(#Query("lat")lat: String, #Query("lon")lon: String, #Query("APPID") app_id: String): Call<DataFromLocation>
}
Client
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private var BASE_URL = "https://api.openweathermap.org/"
val retrofitInstance: Retrofit?
get(){
if(retrofit == null){
retrofit = retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
MainActivity()
class MainActivity: AppCompatActivity {
fun getDataFromApi(cityName: String) {
val service = RetrofitClientInstance.retrofitInstance!!.create(GetWeatherService::class.java)
// Service 1
service.getCurrentWeather(cityName, APP_ID).enqueue(object: Callback<MainWeather>{
override fun onFailure(call: Call<MainWeather>, t: Throwable) {
}
override fun onResponse(call: Call<MainWeather>, response: Response<MainWeather>) {
val weather = response.body()
val currentWeather = CurrentWeather(
weather!!.main.temp,
weather.weather[0].description,
weather.name,
weather.weather[0].main
)
updateCurrentWeatherUI(currentWeather)
}
})
service.getAllWeather(cityName, APP_ID).enqueue(object: Callback<ListWeather>{
override fun onFailure(call: Call<ListWeather>, t: Throwable) {
}
override fun onResponse(call: Call<ListWeather>, response: Response<ListWeather>) {
val weather = response.body()!!.list
for(item in weather){
val weatherList = NextFiveDayWeather(
item.dt,
item.main.temp,
item.weather[0].description
)
weatherArray.add(weatherList)
updateUI(weatherArray)
}
}
})
}
}
Data class
data class MainWeather (
var dt: Long,
var main: MainDTO,
var weather: List<WeatherDTO>,
var dt_txt: String,
var name: String
)
data class WeatherDTO (var main: String, var description: String, var icon: String)
data class ListWeather (#SerializedName("list") var list: List<MainWeather>)
I can’t test the request to api and I can’t fill the classes with fake data?
Tell me please, what should I do?
What should I study? And if you are not difficult can give advice for the future?
You can download the software Postman to test api endpoints: https://www.getpostman.com/
I am facing problem while sending json object body using retrofit to the server. Below is the error.
Failed to invoke public
com.nitesh.brill.saleslines._User_Classes.User_PojoClass.UpdatePreviousDetails()
with no args
code snippet
// Api endpoint
#Headers("Content-Type: application/json")
#POST("UpdatePreviousDetails/{Id}")
fun updatePreviousDetails(#Path("Id") Id: Int, #Body updateDetails :UpdatePreviousDetails): Call<UpdatePreviousDetails>
//pojo class
package com.nitesh.brill.saleslines._User_Classes.User_PojoClass
import java.util.*
/**
* Created by Nitesh Android on 16-08-2017.
*/
class UpdatePreviousDetails(
var CompanyName: String? = null!!,
var Designation: String? = null!!,
var DateOfJoin: Date? = null!!,
var DateOfLeaving: Date? = null!!,
var SectorPreviouslyWorked: String? = null!!,
var Id: Int? = null!!
) {
}
//sending data
val details = UpdatePreviousDetails("rr", "asm", date, date, "Pharmaceuticals",3)
val call = apiEndpointInterface!!.updatePreviousDetails(5, details)
call.enqueue(object :Callback<UpdatePreviousDetails> {
override fun onResponse(call: Call<UpdatePreviousDetails>?, response: Response<UpdatePreviousDetails>?) {
objUsefullData.showSnackBar("success")
UsefullData.Log("============="+response!!.body().toString())
}
override fun onFailure(call: Call<UpdatePreviousDetails>?, t: Throwable?) {
objUsefullData.showSnackBar("fail")
UsefullData.Log("============="+t)
}
})
I am using kotlin language
Your UpdatePreviousDetails class has to have a constructor with no params to enable Gson (inside Retrofit) to convert your object into JSON.
EDIT
class UpdatePreviousDetails() {
var CompanyName: String? = null
var Designation: String? = null
var DateOfJoin: Date? = null
var DateOfLeaving: Date? = null
var SectorPreviouslyWorked: String? = null
var Id: Int? = null
constructor(
CompanyName: String?,
Designation: String?,
DateOfJoin: Date?,
DateOfLeaving: Date?,
SectorPreviouslyWorked: String?,
Id: Int?
) : this() {
this.CompanyName = CompanyName
this.Designation = Designation
this.DateOfJoin = DateOfJoin
this.DateOfLeaving = DateOfLeaving
this.SectorPreviouslyWorked = SectorPreviouslyWorked
this.Id = Id
}
}