In one of my projects, when i calculate the grand total
shippingDetails.grandTotal = 3.448873577E7
instead of 34488749.7
where grandTotal is of type Double
The above object is in my data class and i need to pass as request body for a request, but it fails since it's having scientific notation.
#POST(AppConstants.Network.CONFIRM_PENDING_SHIPPING)
fun confirmPendingShipping(
#Header("Authorization") token: String,
#Body shippingItems: RequestConfirmShipping
): Single<ResponsePendingShipping>
I'm passing it as body of post using Retrofit
Related
I have to make a post request using retrofit, but the URL for this request comes from another request (GET), and the URL comes as a complete endpoint (i.e: https://pod-000-1005-03.backblaze.com/b2api/v2/b2_upload_file?cvt=c001_v0001005_t0027&bucket=4a48fe8875c6214145260818).
How can i make a retrofit request directly to this endpoint?
How im creating the retrofit instance:
fun getUploadApi(uploadUrl: String): B2UploadApi {
return Retrofit.Builder()
.baseUrl(uploadUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(B2UploadApi::class.java)
}
this throws an exception since the url doesnt end with '/'
And the POST request:
#POST
suspend fun uploadFile(
#Header("Authorization") authToken: String,
#Header("X-Bz-File-Name") fileName: String,
#Header("Content-Length") fileSize: Int,
#Header("Content-Type") mimeType: String,
#Header("X-Bz-Content-Sha1") sha1: String,
#Body byteArray: ByteArray
): Response<UploadResponse>
As mentioned in documentation , #url will override base url which you have mentioned at time of retrofit object creation
So you just need to use #url annotation along with method in retrofit service
documentation - https://square.github.io/retrofit/2.x/retrofit/retrofit2/http/Url.html
example -
https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
I am using retrofit2 for network calls in my project. For a post method I need to pass request body data. So decided to use #Body annotation. This converts the given Gson.JsonObject to Array like below mentioned.
Retrofit dependencies used
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
Expected
{
"key1": "value1",
"key2": "value2",
"key3": value3
}
But got converted like
[{
"key1": "value1",
"key2": "value2",
"key3": value3
}]
This is my API service call
#Headers("Content-Type: application/json")
#POST("Account/LoginAsync")
fun doLogin(#Body payLoad: JsonObject): Call<JsonObject>
And data class is
data class LoginRequest(
val email: String,
val password: String,
val rememberMe: Boolean
)
Internally what I am doing is
From the specified values in LoginRequest generating org.json.JSONObject like below
private fun generateLoginPayload(
email: String,
password: String,
rememberMe: Boolean
): JSONObject {
return JSONObject().apply {
put(LOGIN_EMAIL, email)
put(LOGIN_PASSWORD, password)
put(LOGIN_REMEMBER_ME, rememberMe)
}
}
And then
val payload = generateLoginPayload(email, password, rememberMe)
val requestCall = ApiService.create()
.doLogin(JsonParser().parse(payload.toString()) as JsonObject)
After enqueuing the request call got error response as 400 (bad request).
I have checked from the debug values.
What is the possible reason for it ?
Solution 1
If you're trying to send a json payload containing the email, password, and rememberMe option to the Account/LoginAsync endpoint you don't need the generateLoginPayload function.
First, refactor your API service function to something like this:
#Headers("Content-Type: application/json")
#POST("Account/LoginAsync")
fun doLogin(#Body payLoad: LoginRequest): Call<JSONObject>
Then update the payload
val payload = LoginRequest(email, password, rememberMe)
val requestCall = ApiService.create().doLogin(payload)
Solution 2
If you really want to use the JSONObject object in the request body there's no need to convert the payload object to a string, only to later use your JsonParser to convert it back to JSONObject. Just update the last part of the code to:
val payload = generateLoginPayload(email, password, rememberMe)
val requestCall = ApiService.create().doLogin(payload)
I'm not familiar with the JsonParser object. If it's a custom class, please also include the body of the parse method so we can inspect it (if the above solutions don't work).
I use Retrofit (v2.9.0) and Moshi (v1.11.0) in my app. I try to call an endpoint this way:
#FormUrlEncoded
#PATCH("anime/{anime_id}/my_list_status")
fun updateListStatus(
#Path("anime_id") animeId: Long,
#Field("num_watched_episodes") nbWatchedEpisodes: Int,
#Field("score") score: Double,
#Field("status") watchStatus: WatchStatus,
): Single<MyListStatus>
But the WatchStatus->Json conversion is not working as expect. WatchStatus is a simple enum class:
enum class WatchStatus {
COMPLETED,
DROPPED,
ON_HOLD,
PLAN_TO_WATCH,
WATCHING,
}
and I created a custom adapter because my app uses uppercase enum names while the back-end uses lowercase names:
class AnimeMoshiAdapters {
/* Others adapters */
#ToJson
fun watchStatusToJson(watchStatus: WatchStatus): String =
watchStatus.toString().toLowerCase(Locale.getDefault())
#FromJson
fun watchStatusFromJson(watchStatus: String): WatchStatus =
WatchStatus.valueOf(watchStatus.toUpperCase(Locale.getDefault()))
}
I create my Moshi instance this way:
Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.add(AnimeMoshiAdapters())
.build()
and my Retrofit instance uses it (with Koin injection):
Retrofit.Builder()
.baseUrl(get<String>(named("baseUrl")))
.client(get(named("default")))
.addConverterFactory(MoshiConverterFactory.create(get()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
When parsing a Json to create a WatchStatus enum the adapter is used. It is noticeable because the call fails with an error "com.squareup.moshi.JsonDataException: Expected one of [COMPLETED, DROPPED, ON_HOLD, PLAN_TO_WATCH, WATCHING]" if I remove my custom adapter.
When I try to call the endpoint specified above the transformation of a WatchStatus in Json is wrong and the enum name stay in Uppercase, meaning my custom adapter is not used. If I check the Retrofit logs I can see that it send "num_watched_episodes=12&score=6.0&status=ON_HOLD", so the status is not converted in lowercase.
If I try to manually convert a WatchStatus in Json using the same Moshi instance it works as expected, so I believe my custom adapter implementation is correct.
How can I make Retrofit uses my custom Moshi adapter in this call?
Moshi adapters' toJson apply to the retrofit requests' body (== params annotated with #BODY), not query parameters (== params annotated with #FIELD). And it is correct and expected behavior, as query parameters are by standards not expected to be JSON formatted strings (eventhough in your case it's just a String). If your server expects the status field as query parameter, then there is no other way than to provide it lowerCased by yourself:
#FormUrlEncoded
#PATCH("anime/{anime_id}/my_list_status")
fun updateListStatus(
...
#Field("status") watchStatus: String
): Single<MyListStatus>
and feed your updateListStatus with already lowerCased value:
updateListStatus(..., COMPLETED.name.toLowerCase())
If you have no influence on the server's implementation, then skip the rest of this post.
If you want to utilize your custom adapter's toJson function, your server needs to change the request to accept JSON body instead of query params, say like this:
PUT: anime/{anime_id}/my_list_status
BODY:
{
"anime_id" : Long,
"num_watched_episodes" : Int,
"score" : Double,
"status" : WatchStatus
}
Then you would create a data class for the body, say named RequestBody, then you could change your request to:
#PUT("anime/{anime_id}/my_list_status")
fun updateListStatus(
...
#BODY body: RequestBody
): Single<MyListStatus>
in which case your custom adapter will take effect and transform the WatchStatus inside the RequestBody by its defined toJson logic.
I am creating a retrofit get request where i need to pass a data base query and some spacial character like '$' in URL in kotlin. But I am getting error.
java.lang.IllegalArgumentException: URL query string must not have replace block. For dynamic query parameters use #Query.
This is URL which I am using in postman but cant in retrofil
https://someURL.com?customParam=true&pageSize=100&query=$filter=(drivercell eq'1111111119')$orderby=creationTimedesc&withTotalPages=true
This is the code of calling retrofit method
val restServiceModel = DRestServiceModel.create()
val model = restServiceModel.getTripsData("Basic bWs6SU9UMTIzNCM=", "application/json", "\$filter=(drivercell%20eq'1111111119')")
This is method
#GET("inventory/managedObjects?customParam=true&pageSize=100&{query}\$orderby=creationTimedesc&withTotalPages=true")
fun getTripsData(#Header("Authorization") token: String, #Header("Content-Type") contentType: String, #Query("query", encoded = true) query : String): Single<TripsResponseModel>
Please help me.
Problem is you are trying to put Path param in a middle of a query while supplying it via another Query. You should rework your request. Try something like:
#GET("inventory/managedObjects")
fun getTripsData(#Header("Authorization") token: String,
#Header("Content-Type") contentType: String,
#Query("customParam") customParam: Boolean?,
#Query("pageSize") pageSize: Int?,
#Query("query", encoded = true) query: String,
#Query("withTotalPages") withTotalPages: Boolean?): Single<TripsResponseModel>
And use it like:
val model = restServiceModel.getTripsData("Basic bWs6SU9UMTIzNCM=", "application/json", true, 100, "your query_goes here", true)
This way should it work.
#Headers("Accept: application/json")
#POST("/api/v1/search/filter")
#FormUrlEncoded fun searchFilter(#Field("leaf_id") leaf_id: Int,
#Field("lang") lang: String,
#Field("token") token: String,
#Field("ud_id") ud_id: String,
#Field("min_price") min_price: Float,
#Field("max") max: Float,
#Field("page") page: Int,
#Field("per_page") per_page: Int,
#Field("properties") properties:JSONObject): Observable<RespProductsInCatg>
here in the #Field("properties") I'm trying to send json object but getting failed response.
In Postman its returning proper response
Try to use a data class for parameters to be passed in properties field.
Then pass an object of that data class, and use #Body instead of #Field for properties.
Try creating whole request as an object and add the same as #Body param without using #Field values.
Something like
data class Request(var leaf_id:Int,
var lang: String,
.
.
.
var properties: List<Property>);
and
data class Property(var param1:Int, .... );
construct the Request Object and try adding just that as #Body param.