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).
Related
I am using the retrofit library for network calls. In this, I need to pass Body in GET Method. But I am getting the error while I am passing this one. In Postman it is working while passing Body for GET Method.
#GET("http://192.168.0.141:3000/api/contacts/{page_num}/{limit}")
fun getAllContacts(#Path("page_num") page_num:Int,#Path("limit") limit:Int,#Body reqBody:ContactsInpRequest):Call<AllContactsDataResponse>
I am calling get method by passing body. But I am getting the below exception.
java.lang.IllegalArgumentException: Non-body HTTP method cannot contain #Body.
GET method does not contain body like the POST does. Here you can learn more about REST methods: https://restfulapi.net/http-methods/
EDIT: I see that you said that it works in Postman so take a look at this:
*CAN GET request have a body?
In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. ... Yes, you can send a request body with GET but it should not have any meaning.*
java.lang.IllegalArgumentException: Non-body HTTP method cannot contain #Body
This means your #GET or #DELETE should not have #Body parameter. You can use query type url or path type url or Query Map to fulfill your need. Else you can use other method annotation.
#Headers("Content-Type: application/json")
#GET("helper-url")
fun getHelperUrl(
#Query("api_token") apiToken: String,
#Query("authtype") authType: String,
#Query("channel") channel: String
): Call<ResponseHelperUrl>
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
I'm using Twitter end point API to follow another user. The API is:
https://api.twitter.com/1.1/friendships/create.json?follow=&screen_name=&user_id=
with Authorization header passed as:
#Headers("Authorization: OAuth oauth_consumer_key=DC0sePOBbQ8bYdC8Smg,oauth_signature_method=HMAC-SHA1,oauth_timestamp=1502774524,oauth_nonce=175308858,oauth_version=1.0,oauth_token=712057165-iQB4b4Q0hsNmHsAxiW4X5UF5xVB6JmKOPhxnW,oauth_signature=X0GExH5DBVgVv49jkO3LwfX8%3D")
#POST()
#FormUrlEncoded
Call<ResponseBody> followUser(#Url String url, #Field("follow") boolean follow, #Field("screen_name") String screenName, #Field("user_id") String userId);
in Retrofit API call from Android. This works fine. But the Auth header has to be generated dynamically for every logged in user. How to achieve that?
You can use #Header annotation in parameter list. Please check the official documentation.
Replaces the header with the value of its target.
#GET("/")
Call<ResponseBody> foo(#Header("Accept-Language") String lang);
Header parameters may be null which will omit them from the request.
Passing a List or array will result in a header for each non-null
item.
Note: Headers do not overwrite each other. All headers with the same
name will be included in the request.
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... :-)
I am trying to make an API call to a mobile backend with Retrofit 2.0. In my API call, i have to make necessary call to this URL
https://api.backendless.com/v1/data/Users?where=followings.objectId=%270B3BA7F9-260F-B378-FF9A-3C2448B8A700%27
To form this URL in Retrofit i have been using below interface
#GET("Users?where=")
Call<List<User>> getFollowers(#Query("followings.objectId") String objectId);
This interface call puts an ampersand before the query parameters and generates a URL like below
https://api.backendless.com/v1/data/Users?where=&followings.objectId=%270B3BA7F9-260F-B378-FF9A-3C2448B8A700%27
I tried to overcome this with Path annotation but i keep getting "URL query string must not have replace block. For dynamic query parameters using #Query" error.
API that i am trying to connect requires a "where=" clause for filtering by design. I have no permission to change that. What i want is somehow tell Retrofit not to put an ampersand sign before the query parameter or any workarounds for this issue.
Any help is appreciated.
For those who seek for similar answers, I came up with below solution
I declared my interface with #Url
#GET
Call<List<User>> getFollowers(#Url String objectId);
and generated related URL part as an extension method
public String toFollowerRequest(){
return RestGlobals.BASE_URL + "Users?where=followings.objectId=%27" + objectId + "%27";
}
#GET("{path}")
Call> getFollowers(#Path("path") path, #Query("followings.objectId") String objectId);
getFollowers("Users?where=", ...)