Retrofit: what is different between #Field and #Body - android

In some post request, I don't know when to use #Field, when to use #Body.
Like whats the difference between:
#POST("users/register")
Call<String> register(#Body RequestBody registerRequest);
and:
#POST("users/register")
Call<String> register(#Field String id, #Field String pass);
Can I use #Body instead of #Field, and reverse ? If not, why ? And how to know this case use #Body, other case use #Field ?
Can you please give me some case and explain, thank you.

#Body – Sends Java objects as request body.
#Field – send data as form-urlencoded. This requires a #FormUrlEncoded annotation attached with the method.
The #Field parameter works only with a POST. #Field requires a mandatory parameter. In cases when #Field is optional, we can use #Query instead and pass a null value.

Both are used for posting data only, but they have following difference -
The #Body annotation defines a single request body.
interface Foo {
#POST("/jayson")
FooResponse postJson(#Body FooRequest body);
}
That means if you are using #Body, it should be only parameter. It is helpful when you have already a JsonObject and you want to send it as it with you api call.
Another way is, you can send data using #Field and send the Place object as a JSON string.
#POST("/post/addphoto/")
public void addImage(#Field("image_url") String url, #Field("caption") String caption, #Field("google_place_id") String placeId, #Field("facebook_place") String place, Callback<UploadCallBack> response);
Hope it will help... :-)

Related

What is the difference between #Field and #Query in retrofit

In some POST requests, I don't know when to use #Field with FormUrlEncoded and when to use #Query
For Example:
#POST("list-products-for-sale")
Call<ListAllProductsResponse> getNewProducts(#HeaderMap Map<String,
String> headers,#Query("lastProductId") String lastProductId);
When I tried to use #Field here it was not responding properly and when I switched it to #Query it's working great.
I want to know why #Field isn't working while Query can work perfectly and I did tested in POSTMAN where I sent the data as a formurlencoded and it's giving me the results fin.
EDIT
BTW I'm passing Content-Type:application/json, Accept: application/json with an Authorization key
#Field is used to send #FormUrlEncoded request in Retrofit which hides your parameter and not attach with url to provide security.Used for POST request.
#Query parameter appended to the URL.
If you are using #Field request than it will hides your parameter and not append with the url.
If you are using #Query request than all your parameter is append to your request and visible to users.
Depend on your api request you have to use one of above annotation. If the api request accept the #FormUrlEncoded data than use #Field or if they want to attached it with url than use #Query.
You can get more information from below link :
1) https://square.github.io/retrofit/2.x/retrofit/index.html?retrofit2/http/Query.html
2) https://square.github.io/retrofit/2.x/retrofit/retrofit2/http/Field.html

How to upload photo in android retrofit 2?

I can't upload photo to server. java.lang.IllegalArgumentException: #Field parameters can only be used with form encoding. (parameter #1). Please help me, how to solve or any other suggestions. postman
#FormUrlEncoded
#Multipart
#POST("qq/api/xxxx")
Call<Custom> postCustom(#Field("Id") String Id,
#Part MultipartBody.Part file,
#Field("Status") String Status);
Change your request interface like this
#Multipart
#POST("qq/api/xxxx")
Call<Custom> postCustom(
#Part("Id") String Id,
#Part MultipartBody.Part file,
#Part("Status") String Status);
You cannot use both #FormUrlEncoded and #Multipart on a single method since an HTTP request can only have one Content-Type. #FormUrlEncoded and #Multipart are both content types.
From Jake Wharton
You can have to use FormUrlEncodedTypedOutput as a #Part argument for the form encoded part and build it up yourself. The annotation on the method is for the outermost encoding which in this case is multipart.
References
I have added also these issue reference which is really helpful for you to understanding what you are about to change in your http request method
https://github.com/square/retrofit/issues/662
https://github.com/square/retrofit/issues/1210

Multiple Parameter in REST-PUT with both #Path and #Body

REST-API PUT with both #Path and #Body as input parameters
I am trying to create an API PUT method which should use both #Path and #Body as input parameters but cannot find the exact example(sample code) to achieve that.
This is a sample for your requirement.
#PUT("/api/{username}")
void putAPICall(#Path("username") String username, #Body RequestBody params);

Retrofit2: Use Body vs Query

I have a web-service method called change. I send UpdateStatusRequest objects to this web-service which defined as below:
public class UpdateStatusRequest {
private String Status;
public UpdateStatusRequest(String status) {
Status = status;
}
public String getStatus() {
return Status;
}
}
When I use below API deceleration:
#POST("StatusUpdate")
Call<Status> change(#Query("Status") String status);
and then calling statusApi.change(request.getStatus()), it works well. It will call http://server-url/StatusUpdate?Status=Ready, when I pass Ready as status.
But using below declaration
#POST("StatusUpdate")
Call<Status> change(#Body UpdateStatusRequest status);
and then calling statusApi.change(request), it will call http://server-url/StatusUpdate and sends Status in request body. This will lead to 404 status code with error prompt Not Found.
I want to know what's wrong with my second call (since I supposed #Body acts like several #Query parameters which bundled together in the same object)?
In Retrofit,
#Body doesn't same as #Query.
#Body – Sends Java objects as request body.
#Query- Query parameter appended to the URL.null values are ignored.
But #Field is almost similar to #Body tag.
#Field – Send data as form-urlencoded. The #Field parameter works only with a POST.
For Example:
#POST("StatusUpdate")
Call<Status> change(#Field("Status") String Status);
But in your case, Your server is expecting the params to be passed in
the URL(#Query).
Hope this explanation help.
#Body doesn't act like several #Query parameters. These are two different ways of sending data in a request.
The differences are pretty much already described in your question. With #Query,it will append the URL with the query params you pass, as in http://server-url/StatusUpdate?Status=Ready. Instead, if you use #Body, the params will be added to the body request, so your URL will have no params, as in http://server-url/StatusUpdate, and your body request will be Status=Ready.
Based on the results you got, your server is expecting the params to be passed in the URL(#Query).

Using #Path and #Url paramers together with retrofit2

I'm using retrofit2 to comunicate with a webapi.
I need to set the URL of the webapi dynamically beacuase the user can change it, so i use the #Url annotation to specify it like this:
#POST
Call<LoginResponse> login(#Url String url, #Body LoginRequest user);
In one of the operations, i need to send some path parameters to the server, so i do this:
#GET
Call<DataResponse> getOrder(#Url String url,
#Header(WEBAPI_EMAIL_HEDER) String email,
#Header(WEBAPI_TOKEN_ID_HEDER) String token,
#Path("id") int id);
When i call the operation getOrder(...), an exception is rised by retrofit because i am not suppoused to use #Url and #Path parameters in the same operation.
This is the exception:
java.lang.IllegalArgumentException: #Path parameters may not be used with #Url. (parameter #4)
One solution is to replace the id parameter on the url and use only the #Url parameter in the invokation. But i think this is not the best way, beacuase i will be doing this with all the operations with #Path parameters.
Is there any other cleaner solution? Maybe using another retrofit2 annotation?
Thanks in advance.
As described in the post Retrofit 2 - Dynamic URL, the #Url notation assumes that the String is a fully defined URL and does not check whether it contains #Path variables.
To use the #Path annotation, you have to configure an endpoint URL and include the #Path variable inside the #GET() annotation.
There is a workaround. Incase of a dynamic Url with some variable path, we can define a string format with paths denoted by %s arguments.
E.g:
Suppose the dynamic url with path is : https://www.example.com/users/{id}/whoami
Here we can just replace {id} with %s. So now it becomes,
val formatString = https://www.example.com/users/%s/whoami
Now we can use it as a format string and replace it with required id.
val url = formatString.format(id)
and in the retrofit interface, use #Url parameter in the function.
interface AnyService {
fun whoAmI(#Url url:String): Call<ResponseBody>
}
Incase you are using MVVM architecture, you can call the formatting code in the concerned repository.

Categories

Resources