I have an Android application acting as a client to my back end server.
I am doing a POST http request with a help of Retrofit lib with a String in the body.
Problem is, Retrofit is most likely escaping double quotes when using GSON builder.
That results in a field in my DB containing double quotes, example: "example_gcm_token".
I need to know whether I should handle that on server side or on client side and how to do that.
I assume it shouldn't be on the server side as it would mean I have to remove escaped quotes for every single endpoint.
#POST ("/Maguss/users/{userId}/gcmtoken")
Call<Void> setGcmToken(#Path("userId") Long userId, #Body StringEntity gcmToken);
I would try to replace the StringEntity with a POJO:
public class SetGcmTokenRequest {
#SerializedName("gcmtoken")
private String gcmToken;
public String getGcmToken() {
return gcmToken;
}
public void setGcmToken(String gcmToken) {
this.gcmToken = gcmToken;
}
}
And change the interface like this:
#POST ("/Maguss/users/{userId}/gcmtoken")
Call<Void> setGcmToken(#Path("userId") Long userId, #Body SetGcmTokenRequest setGcmTokenRequest);
Related
I'm using Retrofit 2 to call API in Android App. I have a API, using POST, which have a String param in Query Tag. I do everything like doc suppose and I test this API successfully in Test Page. I can run another API correctly so the problem is not the way I use Retrofit 2.
Here is my interface:
#POST("/users/{userId}/get_list_friends")
Call<GetListFriendDataResponse> getListFriend(#Path("userId") int userId, #Query("list") String list, #Query("page") int page, #Query("size") int size, #Header("hash") String hash);
Here is my implementation:
ArrayList<String> id = new ArrayList<>();
id.add("4782947293");
JSONArray jsonArray = new JSONArray(id);
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("list", jsonArray);
} catch (JSONException e) {
e.printStackTrace();
}
String list = jsonObject.toString();
Log.e(TAG, "list: " + list);
apiInterface.getListFriend(21, list, 1,1,"AHHIGHTJGI").enqueue(new Callback<GetListFriendDataResponse>() {
#Override
public void onResponse(Call<GetListFriendDataResponse> call, Response<GetListFriendDataResponse> response) {
Log.e(TAG, " response code: "+ response.code());
}
#Override
public void onFailure(Call<GetListFriendDataResponse> call, Throwable t) {
}
});
I always get response code: 400 when use this API.
I'm focusing the "list" var. "list" is a JSON text but I wonder if method "jSon.toString()" is right to get a String from a JSONObject, which can using in Retrofit 2. List param form is:{"list":["12332"]} .
Please help me!
Questions
1) Why you are creating JSONObject and JSONArray on your own?
2) You are creating string using whatever you are creating json.
Eg: {list:["123","456"]}
you are trying pass whole json, I think, instead you need to pass just the array of string to the list key.
Request Sending
{
list:["123","456"]
}
suppose the above json is the request you want to send to server
Now, create model class goto http://jsonschema2pojo.org and paste your json and select json and gson at right side and click on preview.
It will show the classes to map you json to model. Use this model class to set the list to the key in your json
I found my problem. The JSON text contains some special character so I need to convert them to URL encode.
Correct request URL like this:
http://54.169.215.161:8080/users/29/add_friend?list=%7B%22list%22%3A%5B%2215536%22%5D%7D&platform=google
By using Retrofit 2, it uses the URL:
http://54.169.215.161:8080/users/29/add_friend?list={%22list%22:[%2215536%22]}&platform=google
So I get Bad Request Response code.
Retrofit 2 also provides method to convert char sequence to URL encode but it 's not enough. So I don't use Retrofit 's convert method by using this code: encode= true.
so my interface is:
#POST("/users/{userId}/get_list_friends")
Call<GetListFriendDataResponse> getListFriend(#Path("userId") int userId, #Query(value = "list",encoded = true) String list, #Query("page") int page, #Query("size") int size, #Header("hash") String hash);
And I manually convert JSON text to URL encode by code:
list = list.replace("{", "%7B");
list=list.replace("]", "%5D");
list=list.replace("[", "%5B");
list=list.replace(":", "%3A");
list=list.replace("}","%7D");
list = list.replace("\"", "%22");
That's all. Now I can get data by using API.
Suggestion: If u have the same problem, check the URL retrofit return in response and compare to correct URL to see special character, which is not converted to URL encode.
I believe it's Retrofit that's adding the extra slash to the key value pairs when calling the service with serialized JSON data.
I have a hash map object to be passed as a multipart string, and I'm converting it to JSON string using Gson.
public static String getJsonString(Object object) {
gson = new Gson();
jsonString = gson.toJson(object);
return jsonString;
}
I have the retrofit builder like
Retrofit.Builder()
.baseUrl(path)
.addConverterFactory(GsonConverterFactory.create())
.client(trustCert(context))
.build();
Passing the JSON data as
Call<ResponseBody> responseBodyCall = ApiClient.getInstance(context).getApiService().uploadData(getJsonString(params));
Api interface:
#Multipart
#POST("upload")
Call<ResponseBody> uploadData(#Part("data") String data);
When we debugged on the server side, the received json data has extra slashes in it. For example, it's supposed to be like \"{\"key1\", \"value\"}\" but it is being serialized as \\"{\\"key1\\", \\"value\\"}\\". I have put a breakpoint just before the api call, and the data is all good, but on the server side it's weird.
I've just started working with Retrofit2 and the API I'm consuming wraps all valid responses in a "response" object as shown below. I need to tell Retrofit to parse only the values within response without actually nesting them inside another object. For the login code, I'm also faced with the issue of getting a String which I want to convert to an actual time stamp.
This is a sample response from a login request:
{
"status":"success",
"response":{
"token":"test_token",
"expires":"1485217863"
}
}
In the above the only two actual values are:
token
expires
I'm hoping to end up with something like what is shown below.
public class Token {
#SerializedName("token")
String token;
#SerializedName("expires")
Timestamp expires;
public User(String token, String expires ) {
this.token
this.expires = //conversion code omitted.
}
}
You have a couple of options here. You can either use a custom serialiser/deserialiser, type adapters, or you can simply use pojos and unwrap the result yourself.
Let me start with the easiest solution I can think of. Picture you have these classes:
public class ResponseData<T> {
#SerializedName("status")
#Expose
String status;
#SerializedName("response")
#Expose
T response;
public T getResponse() {
return response;
}
// getters and setters and friends
}
public class Token {
#SerializedName("token")
#Expose
String token;
#SerializedName("expires")
#Expose
Timestamp expires;
public Token(String token, String expires) {
this.token = token;
this.expires = expires;
}
}
So one first thing to notice is the use of #Expose. This is a nice to have, but not extremely necessary. It helps you out when you have custom serialisers.
I assumed that you can have multiple api endpoints that return the same kind of body, where the json is:
{
"status":"success",
"response":{
// Can be anything
}
}
And as you can see the response can be anything.
You can then make your retrofit calls return ResponseData<Token> and in your callbacks you can check the value of status and see if you can do getResponse to unpack the result. The advantage of this approach is that you can reuse ResponseData fairly easily.
Another approach is to use custom serialisers or type adapters. This is in my opinion more laborious, but still a valid approach. I think the answer here is quite extensive and explains how you can do this to get the nested object inside response.
To prepare retrofit to use the type adapters, you'll need to inject a configured Gson instance into it. Here's how:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Token.class, new YourTypeAdapter())
.create();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
// ....
As you can see, we pass the created gson with your type adapter to the GsonConverterFactory used by retrofit. This prepares retrofit to serialise and deserialise Token objects using the given type adapter.
I think the main disadvantage with this approach is that if you want to write a generic deserialiser/serialiser/typeadapter it can become complicated quite fast (assuming you won't have only a Token object).
I am just starting out with retrofit for android. I am getting an error when I try to specify 2 fields for the body of a post request:
Multiple #Body method annotations found. (parameter #2) for method
The Call is defined in my API interface file as:
#POST("auth/login")
Call<UserData> login(#Body String username, #Body String password);
And I create the call with:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService service = retrofit.create(APIService.class);
Call<UserData> call = service.login(username, password);
error is generated when the call is created (do not have a chance to execute it).
When I remove one of the body fields it seems to work fine.
Any Ideas?
Using multiple #Body is bad idea, because #Body here means message Body of HTML POST.
(Detail: How to send data in the HTTP request body when using an HTML form?)
I suggest you to define a class containing both username and password, like below.
public class LoginInformation {
String username;
String password;
}
And, fill your information on that class instance, and use that.
#POST("auth/login")
Call<UserData> login(#Body LoginInformation loginInformation);
Any HTTP request is allowed to contain only one message body , if you try adding two #Body parameters you will get this message "Multiple #Body method annotations found."
And you can fix it by following this:
You can send multiple or different type of objects at the same time using HashMap or single type, for example:
HashMap<String, Object> map = new HashMap<>();
map.put("password", "123456");
map.put("username", "Jake Warthon");
or
public class User(){
private String username;
private String password;
public void setUsername(String username){
this.username = username;
}
public void setPassword(String password){
this.password = password;
}
}
User user = new User();
user.setUsername("Jake Warthon")
user.setPassword("123456");
map.put("user", user);
You can append more data (like different type of objects) in a single body if you want (optional)
map.put("user", user);
map.put("authorization", "12uh3u12huhcd2");
map.put("something", someobject);
You have to change the body type of the request to receive a Hashmap or User
#POST("auth/login")
Call<UserData> login(#Body HashMap map);
or
#POST("auth/login")
Call<UserData> login(#Body User user);
Finally you pass the data to service like you already did.
Call<UserData> call = service.login(map);
or
Call<UserData> call = service.login(user);
And remember, the server side have to implement it correctly to receive the data as a map.
I wanna send a list of integer with userName and password to WebService some thing like bellow request
UpdateDocumentState(List<int> documentIds, string userName, string password)
But I don't know How to do that ? Use #Post Or #Put ? use #Query Or #Field ? I googled but didn't find any good example or tutorial which explained these well. ( All tutorial I found was about #GET )
could anyone give me some piece of code , how to do that ?
About the use of #PUT or #POST I think you had to get this information from the WebService developers.
Anyway, here sample code for both of Retrofit annotations with or without Callback response.
#POST("your_endpoint")
void postObject(#Body Object object, Callback<Response> callback);
#PUT("/{path}")
String foo(#Path("path") String thePath);
EDIT:
Object is a custom class which represent the data you had to send to the WebService.
public class DataToSend {
public List<Int> myList;
public String username;
public String password;
}
For example when the #POST annotation declaration will be:
#POST
void postList(#Body DataToSend dataToSend, Callback<Response> callback);
and then you call the method using Retrofit service
yourService.postList(myDataToSend, postCallback);