How to convert POST curl request to Retrofit Request in Android? - java

I'm trying to represent the below curl request in android using Retrofit,
curl -i -X POST \
https://graph.facebook.com/v13.0/1234567890/messages \
-H 'Authorization: Bearer ucuzZCQv9qb--token--0UMHaEhLwvuOW6WvapUGuPAkrDchj' \
-H 'Content-Type: application/json' \
-d '{ "messaging_product": "whatsapp", "to": "9477number", "type": "template", "template": { "name": "hello_world", "language": { "code": "en_US" } } }'
My API Service class
public interface APIService {
#POST("{phoneNoId}/messages")
Call<MsgResponse> SendMsg(#Path("phoneNoId") String phoneNoId,
#Header("Authorization") String authorization,
#Header("Content-Type") String types,
#Body String msgObj
);
}
My API calling place in the MainActivity.class
APIService apiService=RetroInstance.getRetrofitClient().create(APIService.class);
Call<MsgResponse> call=apiService.SendMsg("100798385993185",
"Bearer 5iWIZBiI5vogoNZBKbBJ0oZAibvZBG---token--xIcDPoW",
"application/json",
jsonBody
);
Log.d(TAG, "onCreate: "+call.toString());
call.enqueue(new Callback<MsgResponse>() {
#Override
public void onResponse(Call<MsgResponse> call, Response<MsgResponse> response) {
Log.d(TAG, "onResponse: "+response.toString());
}
#Override
public void onFailure(Call<MsgResponse> call, Throwable t) {
Log.d(TAG, "onFailure: "+t.getMessage());
Log.d(TAG, "onFailure: "+t.getStackTrace().toString());
}
});
here jsonbody I'm getting the json object using gson like below String jsonBody=gson.toJson(msgObj);
My Request is successful but I'm getting 400 error code error message below, what went wrong here, Thank you
onResponse: Response{protocol=h2, code=400, message=, url=https://graph.facebook.com/v12.0/1234567890/messages}

You can pass headers to your Retrofit request using Interceptor. This might not be a perfect way, but it works quite good, These headers are sent with every API request.
First, extend Interceptor and add the header to the request as shown below:
class LoggingInterceptor implements Interceptor {
#Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request newRequest = request.newBuilder()
.header("Authorization","YOUR AUTH CODE HERE")
.header("Content-Type","application/json").build();
return chain.proceed(newRequest);
}
}
Now you can use this custom Interceptor with OkHttpClient :
OkHttpClientBuilder builder = OkHttpClient.Builder()
.addInterceptor(new MyInterceptor());
Use this client while building the Retrofit object:
Retrofit.Builder()
.baseUrl(YOUR_BASE_URL)
.client(okHttpClientBuilder.build())
.build()

Related

Retrofit to GraphQL

I have an Android app that will be using data from GraphQL API via AWS Appsync. I know most resources point to Apollo as the client library to use, but due to our CICD, generating the models is not entirely possible, or I haven't found good examples/documentation on CICD/Apollo/Android. The work around was to create a regular POST request with Retrofit. No matter what I try I can't seem to get anything other than a 400 returned. I have successfully called the API with curl and NodeJS. I am fairly new to Android so any guidance is appreciated.
curl approach (success):
curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:[SOME_API_KEY]" -d '{ "query": "query { listData { dataName } }" }' https://amazon.endpoint.com/graphql
Node approach (success):
async function testApi() {
const headers = {
'x-api-key': 'SOME_API_KEY',
'Content-Type': 'application/graphql'
}
const options = {
headers
};
try {
const b = await axios.post('https://amazon.endpoint.com/graphql',
{ "query": "query { listData { dataName } }" },options)
console.log(b.data)
} catch (error) {
console.log(error)
}
}
testApi();
Android approach (failure):
// class to create
public interface GraphqlService {
#Headers({
"Content-Type: application/graphql",
"x-api-key: SOME_API_KEY"
})
#POST("/graphql")
Call<String> getLockers(#Body String body);
}
// in main code
retrofit = new Retrofit.Builder()
.baseUrl("https://amazon.endpoint.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
GraphqlService service = retrofit.create(GraphqlService.class);
try {
JSONObject paramObject = new JSONObject();
String query = "query { listData { dataName } }";
paramObject.put("query", query);
service.getLockers(paramObject.toString())
.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
System.out.println(response);
}
#Override
public void onFailure(Call<String> call, Throwable t) {
System.out.println(t);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
I am thinking there is something funny at work with the way Gson converts the object? I have verified that the query is right via curl and Node. Once again I am new to Android development and would appreciate any thoughts on this.
I actually found the issue. It was with the use of the Gson objects. Instead of using String I should have been using JsonObject from (Gson). Since I was using the GsonConverterFactory there were weird values showing up when using the JSONObject creator. Hopefully this helps someone out. Also working with Gson, the default class that should be used when serializing objects is JsonObject.

Retrofit Post Multipart form data values are empty

I am trying to post some multipart form data via Retrofit in Android to a web service.
The web services's API expects the following parameters to be passed as field of a multipart form data:
Name Type
company_id text/plain
image image/*
It also expects an authorization token to be passed as a query string parameter.
So i have defined my API interface call like this:
#Multipart
#POST("/companies/uploadImage")
#Headers({
"Content-Type: multipart/form-data",
"Accept: application/json"
})
Call<ServerResponse> companyUploadImage( #Part("company_id") RequestBody companyId, #Part MultipartBody.Part file, #Query("token") String token);
Afterwards this is how i call the above API interface method in a custom class:
RequestBody companyId = RequestBody.create(MediaType.parse("text/plain"), LocalStorage.getInstance().getCompanyId());
File file = new File(postPath);
MultipartBody.Part image = MultipartBody.Part.createFormData("image", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
Call<ServerResponse> uploadProfileImage = router.companyUploadImage(companyId, image, token);
uploadProfileImage.enqueue(new Callback<ServerResponse>() {
#Override
public void onResponse(Call<ServerResponse> call, Response<ServerResponse> response) {
Log.e("Upload Profile Image: ", response.body().getMessage());
}
#Override
public void onFailure(Call<ServerResponse> call, Throwable t) {
Log.e("Upload Profile Image Error: ", t.getMessage());
}
});
The request is sent successfully to the server, so no networking exceptions occur, however the multipart form values, company_id and image, are received empty on the server side
Any idea?
Thank you!
I managed to solve the issue. It seems that the solution was as simple as removing the #Header annotation from the API interface method. So now it looks like this:
#Multipart
#POST("/companies/uploadImage")
Call<ServerResponse> companyUploadImage( #Part("company_id") RequestBody companyId, #Part MultipartBody.Part file, #Query("token") String token);
Maybe someone will find this post helpful.

Retrofit2: How to get binary file inside response body

I get data from a web service that sends files in binary format inside response body. I want to get that binary data.
I'm not gonna write the whole log here because it's too long and it's not helpful but i'm gonna write the first few lines:
The first line of the PDF is this: %PDF-1.4 and the rest is binary characters like this: ��Yi3y�H�w��L�A���mCv��.��K��7����Z0�VP. so it's a PDF file inside response body.
And here's my code to get it:
#Streaming
#Headers({"Content-Type: application/json", "Accept: application/json"})
#POST("/auto/report/download")
Call<String> sendStatementDownloadRequest(#Body DepositStatementsDownloadRequest request);
And this is how i call it:
webservice.sendStatementDownloadRequest(request).enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
Log.i("VVV", "onResponse: " + new Gson().toJson(response));
} else {
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
}
});
The problem is it only gets the first line which is this: %PDF-1.4 and skip the rest of it which is the binary data.
is there anyway to get the whole body?
Call for a ResponseBody object instead of String
Your call should be like this
#Streaming
#Headers({"Content-Type: application/json", "Accept: application/json"})
#POST("/auto/report/download")
Call<ResponseBody> sendStatementDownloadRequest(#Body DepositStatementsDownloadRequest request);

Jersey PathParam with HTTP 405 error

I'm using jersey for my rest server, and I got a HTTP 405 error, when I try to forward POST request to relative GET resource.
#Path("/")
public class MyResource {
#POST
#Path("/{method}")
#Produces(MediaType.APPLICATION_JSON)
public String postRequest(#PathParam("method") String method, #Context UriInfo uriInfo, String body) throws IOException {
JsonParser parser = new JsonParser();
JsonObject root = parser.parse(body).getAsJsonObject();
JsonObject params = root;
if (root.has("method")) {
method = root.get("method").getAsString();
params = root.getAsJsonObject("params");
}
UriBuilder forwardUri = uriInfo.getBaseUriBuilder().path(method);
for (Map.Entry<String, JsonElement> kv : params.entrySet()) {
forwardUri.queryParam(kv.getKey(), kv.getValue().getAsString());
}
return new SimpleHttpClient().get(forwardUri.toString());
}
#GET
#Path("/mytest")
#Produces(MediaType.APPLICATION_JSON)
public String getTest(#QueryParam("name") String name) {
return name;
}
}
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/anythingbutmytest
curl -X GET localhost/mytest?name=jack
These two curl above work fine. But I get a 405 error , when I try to request like this:
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/mytest
javax.ws.rs.NotAllowedException: HTTP 405 Method Not Allowed
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.getMethodRouter(MethodSelectingRouter.java:466)
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.access$000(MethodSelectingRouter.java:94)
......
What should I do?
-------------------------------------Update-------------------------------------
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/mytest
This curl work fine, when I add a post method like below. But I will write a same POST method for every GET Method like that, is there any other solution?
#POST
#Path("/mytest")
#Produces(MediaType.APPLICATION_JSON)
public String postMyTest(#Context UriInfo uriInfo, String body) throws Exception {
return postRequest(uriInfo.getPath(), uriInfo, body);
}
Besides, is there any other way to re-route POST request to a method in the same class without building a new HTTP request?
You should make
#POST
#Path("/mytest")
and not "getTest" method.Reason is below.
Command
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/anythingbutmytest
will accept because of
#Path("/{method}") .
But
curl -X POST -d {"method":"mytest","params":{"name":"jack"}} localhost/mytest
will not accept because of
#GET
#Path("/mytest")
POST does not match GET.

Retrofit: 500 internal server error

I have 500 internal server error, every time when i try to send POST request via Retrofit. When i sending GET request, it sending correctly. I'm sure that with serverside everyting is ok. What's wrong with my code ?
String ENDPOINT = "http://52.88.40.210";
//model for request
FriendModel ff = new FriendModel();
ff.setFriendNumber("380935275259");
ff.setId(516);
ff.setNumber("380936831127");
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(ENDPOINT)
.build();
WayfAPI api = adapter.create(WayfAPI.class);
api.getFriendsLocation(ff, new Callback<List<FriendLocationModel>>() {
#Override
public void success(List<FriendLocationModel> friendLocationModels, Response response) {
for (FriendLocationModel ff : friendLocationModels) {
Log.d("myLogs", "===========Successful==========");
Log.d("myLogs", "Id: " + ff.getId());
Log.d("myLogs", "Number: " + ff.getNumber());
Log.d("myLogs", "GeoLocation: : " + ff.getGeoLocation());
}
}
#Override
public void failure(RetrofitError error) {
Log.d("myLogs", "-------ERROR-------");
Log.d("myLogs", Log.getStackTraceString(error));
}
});
}
Declaration of request:
#Headers({
"Accept: application/json",
"Content-type: application/json"
})
#POST("/api/geo/getLoc")
public void getFriendsLocation(#Body FriendModel friendModel, Callback<List<FriendLocationModel>> response);
Exampe of request and response from Postman:
It seems that in postman you're sending an array of FriendModel, but in your code you're sending a single object.
Just change the object you're sending, and instead of sending a single object, send a List as the server expects
List<FriendModel> friendsList = new ArrayList<FriendModel>();
FriendModel ff = new FriendModel();
ff.setFriendNumber("380935275259");
ff.setId(516);
ff.setNumber("380936831127");
friendsList.add(ff);
You should also change this signature:
public void getFriendsLocation(#Body FriendModel friendModel, Callback<List<FriendLocationModel>> response);
to
public void getFriendsLocation(#Body List<FriendModel> friendModel, Callback<List<FriendLocationModel>> response);

Categories

Resources