I have a DJANGO REST server on my personal network that is set up to get and receive JSON strings to update "statuses" for a project I'm working on. I can GET the information down into my Android application just fine but when I try to PUT information up to the server, I get the error:
Bad Request: /api/users/1/
[28/Oct/2019 21:23:23] "PUT /api/users/1/ HTTP/1.1" 400 107
I think it's the app because I can successfully do a PUT request via Windows Powershell, but it doesn't work on the Android app. Here is the code using the OKHTTP class:
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String content = String.format("{'status':%s}", selectedStatus);
RequestBody body = RequestBody.create(JSON, content);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(IPAddress + "api/users/" + currentUserID + "/")
.addHeader("Content-Type", "application/json")
.put(body) //PUT
.build();
client.newCall(request).execute();
I have tried using the standard HttpURLConnection class, which is what I did for the GET method, but I don't even get a response from the server when I do that. I've tried about a dozen ways to PUT something to the server, but nothing has worked. Any help achieving this goal would be appreciated. Thanks.
For anyone looking at this later, I found the issue. It turns out that in the Windows Powershell, you can use single quotes in the JSON string, but you NEED to use double quotes with the OKHTTP methods. Thus, I changed:
String content = String.format("{'status':%s}", selectedStatus);
to:
String content = String.format("{\"status\":%s}", selectedStatus);
and everything went through perfectly. Hope this helps someone.
Related
I am using an API that is out of my control, as well having joined a development team recently that is using Retrofit1.
I am unable to send the request to the server in the format required, as the server requires multipart form data Body in the following format:
Uniqueidentifier:FileName.jpg:ReroutServerIP:Base64EncodedDocString.
I have tried many different techniques in order to accomplish this task but I cannot find any working method to do this. The server tells me that the message format not supported. Here is my current code (with the url stripped out). Please could someone assist?
#POST("URL")
public Response post_SendData(#Header("Content-Type") String ContentType, #Body String body);
In order to achieve the desired result, I can use postman with no headers and post a file from my system using the form-data post method. In the working postman post, the Key is the formatted string mentioned above and the value is a file selected from my desktop. Please see below for postman (edited to remove urls).
Postman
Thanks a lot guys.
If you want to send a file to the server :
public void uploadPictureRetrofit(File file, Callback<YourObject> response) {
// this will build full path of API url where we want to send data.
RestAdapter restAdapter = new RestAdapter
.Builder()
.setEndpoint(YOUR_BASE_URL)
.setConverter(new SimpleXMLConverter()) // if the response is an xml
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
// SubmitAPI is name of our interface which will send data to server.
SendMediaApiInterface api = restAdapter.
create(SendMediaApiInterface.class);
TypedFile typedFile = new TypedFile(MULTIPART_FORM_DATA, file);
api.sendImage(typedFile, response);
}
And this is the interface SendMediaApiInterface :
public interface SendMediaApiInterface {
#Multipart
#POST("url")
void sendImage(#Part("here_is_the_attribute_name_in_webservice") TypedFile attachments, Callback<YourObject> response);}
I'm trying to send a json post request to some API which in response sends a binary file back.
I'm doing well in Postman:
Header:
Body and result:
And I get the following code from Code section in Postman for Java/OKHTTP
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\r\n \"Text\":\"Hello\",\r\n \"APIKey\":\"MY_API_KEY\",\r\n \"Speaker\":\"Female1\",\r\n \"Format\":\"mp3/32/m\",\r\n \"Quality\":\"quality/normal\"\r\n}");
Request request = new Request.Builder()
.url("http://url/CloudService/ReadText")
.post(body)
.addHeader("content-type", "application/json")
.addHeader("cache-control", "no-cache")
.addHeader("postman-token", "0a1ce7c9-7a95-a2b9-7cde-8a7e6ce58386")
.build();
Response response = client.newCall(request).execute();
But when I use the above code in android it fails, I'm sure that I got Internet permission and the code is executed within an AsyncTask.
I'm not asking about the API or how to send json Post request to some API and get a binary file in response. I've used client.newCall(request).enqueue(new Callback(){//stuff here}); but none works. In response I got a 307 status code (instead of 200 in Postman) and no binary data at all. The API is very unclear and said nothing about the failure and I'm still working on that.
All I'm asking is that does Postman generates equivalent code for OkHttp correctly? and if not what is your suggestion for equivalent of this request in Java/OkHttp?
Just to provide another example, the following is also a working Python requests script to do the same job:
url = 'http://url/CloudService/ReadText'
api_key = 'MY_API_KEY'
body = {
'Text': 'Hello',
'Speaker': 'Female1',
'Format': 'mp3/32/m',
'Quality': 'quality/normal',
'APIKey': api_key
}
header = {
'Content-type': 'application/json'
}
r = requests.post(url, data=json.dumps(body), headers=header)
So after 3 days I found the problem, the server API did not mention that the URL endpoint must be ended with an / and even in their sample code they didn't use one.
It seems that both Postman and Python requests use an / at the end of URL in case of need, but Postman at least does not mention that in the generated equivalent code. Also OkHttp does not operate in the same manner.
However using a trailing / solved the problem.
When posting the same basic JSON from Android or via curling, the result is different.
I'm trying to understand why? I'm assuming there is something about Rails that I don't understand.
Command Line HTTP POST
curl 'http://localhost:3000/mobile/register' -X POST -H 'Content-Type:
application/json' -d '{"user": {"email": "awesome#example.com",
"password":"helloworld", "password_confirmation":"helloworld"}}'
Server logs
Parameters: {"user"=>{"email"=>"awesome#example.com",
"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"},
"registration"=>{"user"=>{"email"=>"awesome#example.com",
"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}}
Android HTTP POST
OkHttpClient client = new OkHttpClient();
JSONObject userObject = new JSONObject();
userObject.put("email", mUserEmail);
userObject.put("password", mUserPassword);
userObject.put("password_confirmation", mUserPasswordConfirmation);
String userString = userObject.toString();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("user", userString)
.build();
Request request = new Request.Builder()
.url(REGISTER_API_ENDPOINT_URL)
.method("POST", RequestBody.create(null, new byte[0]))
.post(requestBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback()...
Server logs
Parameters:
{"user"=>"{\"email\":\"awesome#example.com\",\"password\":\"helloworld\",\"password_confirmation\":\"helloworld\"}"}
Rails Routes:
devise_scope :user do
namespace :mobile do
post '/register', to: 'registrations#create', as: :register
end
end
Differences:
curling hides the password but Android does not
curling adds an additional nested JSON with all the data repeated
I'm not sure what causes these differences?
UPDATE
I checked request.env["CONTENT_TYPE"] in the controller action and I am seeing differences in Content-Type.
Curl -> application/json
Android -> multipart/form-data; boundary=...
Could this cause the issue?
Is it easy to change from the Android side? I added .header("Content-Type", "application/json") to the request but it makes no difference?
I think the Android HTTP POST lacks the Content-Type: application/json header because it sends multipart form data . So the rails app logs it as plain string data instead of parsing it, registering user and filtering password.
Also, in case of curl command, the parsed JSON user object is used to register a user. The repeated log entry is perhaps done during this user registration.
To make both requests equivalent, try using the POST TO A SERVER example given at http://square.github.io/okhttp/.
I have a requirement to get a request body and to perform some logic operations with Retrofit 2.0 before doing enque operation. But unfortunately I am not able to get the post body content from my service call. At present after searching a lot I found only one solution like logging the request that I am posting using Retrofit 2.0 from this method by using HttpLoggingInterceptor with OkHttpClient. I am using the following code to log the request body in the Android Logcat:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BODY);
OkHttpClient httpClient = new OkHttpClient();
httpClient.interceptors().add(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(apiClass);
Problem I am facing with the above method:
Request body are seen only on the default Android Logcat like
02-04 01:35:59.235 5007-5035/com.example.retrofitdemo D/OkHttp:{"nameValuePairs":{"id":"1","name":"chandru","type":"user"}}
But the above method returns only response body in the logcat, I am not able to get it as either String or JSONObject. Eventhough if I could able to get the response body using HttpLoggingInterceptor, my request body will be shown in the Logcat with the tag "OkHttp" all the time even after the application goes into the production (So primarily this leads like a kind of relieving the post data in the logcat).
My Requirement:
I need to get the request body as String or JSONObject or whatever method without reviling the post data in the Logcat.
What I tried:
I tried to fetch the request body even after onResponse from Response<T> response, but though I couldn't able to get it done possible. Please find the code that I am using for this purpose as follows:
Gson gson = new Gson();
String responseString = gson.toJson(response.raw().request().body());
Note: The above converted request body using gson.toJson method returns only the meta data not the request post data.
Kindly help me with this by providing your valuable tips and solutions. I have no idea how to do this. I am completely stuck up with this for the past two days. Please forgive if my question is too lengthy. Your comments are always welcome. Do let me know if my requirement is not clear. Thanks in advance.
I have got it working from this link
Retrofit2: Modifying request body in OkHttp Interceptor
private String bodyToString(final RequestBody request) {
try {
final RequestBody copy = request;
final Buffer buffer = new Buffer();
if (copy != null)
copy.writeTo(buffer);
else
return "";
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
Retrofit dependencies i am using are
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'
compile 'com.squareup.okhttp3:okhttp:3.0.0-RC1'
compile 'com.squareup.okhttp3:logging-interceptor:3.0.0-RC1'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
I too had the similar issue, I had set JSON as #Body type in retrofit, so the namevaluepair appears in front of the raw body, and it can be seen inside the interceptor.
Even though if we log/debug the jsonObject.toString we see the request as correct without the namevaluepair presented.
What i had done was by setting
Call<ResponseBody> getResults(#Body JsonObject variable);
and in the calling of the method i converted the JSONObject to JsonObject by
new JsonParser().parse(model).getAsJsonObject();
I am trying to use DELETE method of HttpMethod. The code that I am using for that is
response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, Response.class);
I am also using JacksonJson for mapping json. The delete functionality returns the json which should be mapped to Response class. But calling the above line doesn't works and gives internal server error with 500 as response code. But, the same API does work with RESTClient in the browser so I guess there is something that I am not doing correctly.
After doing some more research it seems that DELETE method doesn't supports request body. As we had the control over REST API we have changed the request body to be added as parameters. After doing this change the request is working properly.
Hope it helps someone.
A little late to the party I'd like to chime in here as well (document my solution for posterity)
I'm too using spring's rest template, also trying to perform a delete request with a payload AND i'd also like to be able to get the response code from the server side
Disclaimer: I'm on Java 7
My solution is also based on a post here on SO, basically you initially declare a POST request and add a http header to override the request method:
RestTemplate tpl = new RestTemplate();
/*
* http://bugs.java.com/view_bug.do?bug_id=7157360
* As long as we are using java 7 we cannot expect output for delete
* */
HttpHeaders headers = new HttpHeaders();
headers.add("X-HTTP-Method-Override", "DELETE");
HttpEntity<Collection<String>> request = new HttpEntity<Collection<String>>(payload, headers);
ResponseEntity<String> exchange = tpl.exchange(uri, HttpMethod.POST, request, String.class);