I have a retrofit interface (kotlin):
interface Api {
#POST("updates")
fun getDetails(#Header("Authorization") token: Token,
#Body body: RequestBody): Single<ResponseObject>
}
When I call it like this (in java),
List<String> apps = Arrays.asList("app1", "app2");
JSONObject jsonObject = new JSONObject();
RequestBody requestBody;
try {
jsonObject.put("apps", new JSONArray(apps));
}
catch(JSONException e) {
e.printStackTrace();
}
requestBody = RequestBody.create(jsonObject.toString(), MediaType.get("application/json"));
api.getDetails(token, requestBody); // works
It works and returns the proper data.
My problem is that I don't want to create a new RequestBody object for each call, so instead of receiving a RequestBody, I want my interface to receive the JSON Object. When I try this however,
interface Api {
#POST("updates")
fun getDetails(#Header("Authorization") token: Token,
#Header("Content-Type") type: String,
#Body body: JSONObject): Single<ResponseObject>
}
api.getDetails(token, "application/json", jsonObject); // does not work
it returns with a retrofit2.adapter.rxjava2.HttpException: HTTP 400 error.
How do I make this http call with just the JSONObject? I think there may be something wrong with how I am specifying the Content-Type, but I can't figure it out.
Related
I have a requirement to call a rest api post method which accepts two parameters username(optional query param) , inputBody(required param of JSON object but not json string) which is to be passed to "BODY" not query parameter.
These are the steps that I followed to make the call:
created an oauth consumer object that is used to sign the request and httpclient object to execute the
request
consumer = new CommonsHttpOAuthConsumer("abc", "def");
requestConfig = RequestConfig.custom().setConnectTimeout(300 * 1000).build();
httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build();
Creating the post request with json
json_string = " {"id":"erv" , "age": 18 , "country" : "IND"} " // This is the json string
updatereq = new HttpPost("BASEURL/object");
updatereq.setHeader("Id","xyz");
StringEntity input = new StringEntity(json_string);
input.setContentType("application/json");
updatereq.setEntity(input);
Signing the request with OAUTH credentials
`
try {consumer.sign(updatereq); // SIGNING THE REQUEST}
catch(OAuthMessageSignerException e) {
e.printStackTrace();
}
catch(OAuthExpectationFailedException e) {
e.printStackTrace();
}
catch(OAuthCommunicationException e) {
e.printStackTrace();
}
`
Finally executing the request to get the response code
updateresponse = httpClient.execute(updatereq);
int updatehttpResponseCode = updateresponse.getStatusLine().getStatusCode();
System.out.println("post Response Code :: " + updatehttpResponseCode);
if (updatehttpResponseCode == 200)
{
System.out.println("POST request worked);
}
else
{
System.out.println("POST request not worked");
}
```
OUTPUT:
post Response Code :: 400
POST request not worked
I am receving the "http bad request" response for the request I am making.
How can I solve this issue?
should I need to change the request format by specifying any additional headers/parameters to make this work?
Where am I doing wrong?
I want to upload image on retrofit API with other 5 parameters, I tried all methods listed on stackoverflow but didn't succeed. I'm getting 500 Internal Server Error and {"message":"Unexpected token - in JSON at position 0"} this repsonse from server. I tried this in POSTMAN but API is working fine in it. Can you please tell me where I'm doing wrong.
Interface Code.
#Multipart
#Headers({"Content-Type: application/json;charset=UTF-8"})
#POST("uploaddoc")
Call<UploadDocuments> uploadDocuments(#PartMap Map<String, RequestBody> requestBodyMap,
#Part MultipartBody.Part file,
#Header("Authorization") String auth);
API call in activity.
private void uploadImage(String imagePath) {
File file = new File(imagePath);
RequestBody requestFile = RequestBody.create(file, MediaType.parse("multipart/form-data"));
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
Map<String, RequestBody> requestBodyMap = new HashMap<>();
requestBodyMap.put("label", RequestBody.create(encryptedLabel, MediaType.parse("multipart/form-data")));
requestBodyMap.put("role", RequestBody.create(encryptedRole, MediaType.parse("multipart/form-data")));
requestBodyMap.put("userobjid", RequestBody.create(encryptedUserObjId, MediaType.parse("multipart/form-data")));
requestBodyMap.put("whichtype", RequestBody.create(encryptedWhichType, MediaType.parse("multipart/form-data")));
requestBodyMap.put("gsttype", RequestBody.create(encryptedGstinSpinner, MediaType.parse("multipart/form-data")));
Call<UploadDocuments> documentsCall = equibiz_api_interface.uploadDocuments(requestBodyMap, body,"Bearer " + AuthToken);
documentsCall.enqueue(new Callback<UploadDocuments>() {
#Override
public void onResponse(#NotNull Call<UploadDocuments> call, #NotNull Response<UploadDocuments> response) {
UploadDocuments uploadDocuments1 = response.body();
assert uploadDocuments1 != null;
if(response.isSuccessful())
Toast.makeText(VerificationActivity.this, response.message(), Toast.LENGTH_SHORT).show();
else{
try {
assert response.errorBody() != null;
Toast.makeText(VerificationActivity.this, response.errorBody().string(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(#NotNull Call<UploadDocuments> call, #NotNull Throwable t) {
if (t instanceof SocketTimeoutException)
Toast.makeText(VerificationActivity.this, "Socket Time out. Please try again.", Toast.LENGTH_LONG).show();
else
Toast.makeText(VerificationActivity.this, t.toString(), Toast.LENGTH_LONG).show();
}
});
}
Postman Screenshot (Authorization token added into Header part)
I had gone through the Postman screenshot and your code. The code has some problems. Here are the problems and solutions.
The field name for the image/file is documentimages, not file. Change the field name like below.
RequestBody requestFile = RequestBody.create(file, MediaType.parse("multipart/form-data"));
MultipartBody.Part body = MultipartBody.Part.createFormData("documentimages", file.getName(), requestFile);
//^^^^^^ NOTICE THE CHANGES HERE
For text fields (the data you send along with file) set MediaTye as text/plain instead of multipart/form-data. For example,
requestBodyMap.put("gsttype", RequestBody.create(encryptedGstinSpinner, MediaType.parse("text/plain")));
Like the above shown, you need to change it to other text fields as well.
The headers you were using is not correct. If you use #Headers({"Content-Type: application/json;charset=UTF-8"}) as content type here, the Retrofit assumes that the data you send from client to server is JSON, not Multipart Form Data. If you get JSON as the response for the uploaded file from the server, then you can do it like below.
#Multipart
#Headers({"Accept: application/json;"}) // <===== CHANGE HEADER TYPE FROM Content-Tye to Accept
#POST("uploaddoc")
Call<UploadDocuments> uploadDocuments(#PartMap Map<String, RequestBody> requestBodyMap,
#Part MultipartBody.Part file,
#Header("Authorization") String auth);
Check the header part.
If you want to understand more about the headers, refer to this thread
I'm trying Retrofit 2.4.0 in my android application. Where do I have a login activity. I'm trying POST request but it return response code 404 in onResponse method. But It is working perfectly with POSTMAN and I getting correct data in log using HttpLoggingInterceptor. I have tried many methods to overcome this problem. My service code is.
#POST("user_login")
#FormUrlEncoded
Call<ResponseBody> user_login(#Field("email") String email, #Field("password") String password);
#Multipart
#POST("user_login")
Call<ResponseBody> user_login(#PartMap() Map<String, RequestBody> bodyMap);
#Multipart
#POST("user_login")
Call<Status> user_login(#Part("email") RequestBody email, #Part("password") RequestBody password);
#POST("user_login")
Call<JSONObject> user_login(#Body String credentials);
#POST("user_login")
Call<User> user_login(#Body LoginCredentials credentials);
none of above methods aren't working.
I'm posting some other methods Which are I am using too.
#Provides
#Singleton
Cache provideOkHttpCache(TUK application) {
int cacheSize = 10 * 1024 * 1024; // 10 MB
return new Cache(application.getCacheDir(), cacheSize);
}
#Provides
#Singleton
Gson provideGson() {
GsonBuilder builder = new GsonBuilder();
// builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return builder.create();
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.cache(cache);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message -> Logger.wtf("AppModule", message));
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
return builder.build();
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient client) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(TUK.BASE_URL)
.client(client)
.build();
}
but I'm trying other URLs too with GET method are working perfectly.
my base url is like "http://www.example.com/api/"
and my #PartMap() code is
public static Map<String, RequestBody> getMap(LoginCredentials credentials) {
Map<String, RequestBody> map = new HashMap<>();
map.put("email", RequestBody.create(MultipartBody.FORM, credentials.getEmail()));
map.put("password", RequestBody.create(MultipartBody.FORM, credentials.getPassword()));
return map;
}
my HttpLoggingInterceptor logging data is
<-- 404 https://www.example.com/api/user_login (718ms)
content-type: application/json; charset=utf-8
.
.
.
.
{"status":false,"message":"Invalid User Email Or Password."}
<-- END HTTP (60-byte body)
and my onResponse methods logging data is
Response{protocol=h2, code=404, message=, url=https://www.example.com/api/user_login}
At the end my question is how to POST data using Retrofit.
I have stuck at this point. I have tried other solutions from stackoverflow but no help. Please guys help me out this problem. Any help will be appreciated. Thank you.
You dont need to use the multipart body for POST request and it can be easily done by
below code. For example I have an base URL like http://example.com/api/
#FormUrlEncoded
#POST("login")
Call<LoginResponse> login(#Field("email") String email,
#Field("password") String password);
The final call will be redirected to http://example.com/api/login in above case.
So in this case my call will be directed to http://example.com/api/login.
Or the other problem can be in your URL. As your base url is this "http://www.example.com/api/" and by joining the API which API name, it becomes this "http://www.example.com/api//api/user_login" which contains two "//" that can cause error in finding your API because of that it throws 404( which is not found). So please adjust your base URL accordingly. Hope that helps somehow.
Looks like a server mistake, 404 is the error code returned by server. Retrofit has requested success. But any request that the return code is not 200 will be considered a failed request. Retrofit will callback onFailure() method, you can get response in the following way.
#Override
public void onFailure(Call<News> call, Throwable t) {
if(t instanceof HttpException){
ResponseBody body=((HttpException) t).response().errorBody();
}
}
Tell your back end the meaning of 404,it should not be returned here
I have solved this problem by setting builder.setLenient();
complete code is
#Provides
#Singleton
Gson provideGson() {
GsonBuilder builder = new GsonBuilder();
builder.setLenient(); // this line help me to get response.
// builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return builder.create();
}
thanks for your help guys.
Currently I’m having an issue with new Spring 5 WebClient and I need some help to sort it out.
The issue is:
I request some url that returns json response with content type text/html;charset=utf-8.
But unfortunately I’m still getting an exception:
org.springframework.web.reactive.function.UnsupportedMediaTypeException:
Content type 'text/html;charset=utf-8' not supported. So I can’t
convert response to DTO.
For request I use following code:
Flux<SomeDTO> response = WebClient.create("https://someUrl")
.get()
.uri("/someUri").accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToFlux(SomeDTO.class);
response.subscribe(System.out::println);
Btw, it really doesn’t matter which type I point in accept header, always returning text/html. So how could I get my response converted eventually?
As mentioned in previous answer, you can use exchangeStrategies method,
example:
Flux<SomeDTO> response = WebClient.builder()
.baseUrl(url)
.exchangeStrategies(ExchangeStrategies.builder().codecs(this::acceptedCodecs).build())
.build()
.get()
.uri(builder.toUriString(), 1L)
.retrieve()
.bodyToFlux( // .. business logic
private void acceptedCodecs(ClientCodecConfigurer clientCodecConfigurer) {
clientCodecConfigurer.customCodecs().encoder(new Jackson2JsonEncoder(new ObjectMapper(), TEXT_HTML));
clientCodecConfigurer.customCodecs().decoder(new Jackson2JsonDecoder(new ObjectMapper(), TEXT_HTML));
}
If you need to set the maxInMemorySize along with text/html response use:
WebClient invoicesWebClient() {
return WebClient.builder()
.exchangeStrategies(ExchangeStrategies.builder().codecs(this::acceptedCodecs).build())
.build();
}
private void acceptedCodecs(ClientCodecConfigurer clientCodecConfigurer) {
clientCodecConfigurer.defaultCodecs().maxInMemorySize(BUFFER_SIZE_16MB);
clientCodecConfigurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonDecoder(new ObjectMapper(), TEXT_HTML));
clientCodecConfigurer.customCodecs().registerWithDefaultConfig(new Jackson2JsonEncoder(new ObjectMapper(), TEXT_HTML));
}
Having a service send JSON with a "text/html" Content-Type is rather unusual.
There are two ways to deal with this:
configure the Jackson decoder to decode "text/html" content as well; look into the WebClient.builder().exchangeStrategies(ExchangeStrategies) setup method
change the "Content-Type" response header on the fly
Here's a proposal for the second solution:
WebClient client = WebClient.builder().filter((request, next) -> next.exchange(request)
.map(response -> {
MyClientHttpResponseDecorator decorated = new
MyClientHttpResponseDecorator(response);
return decorated;
})).build();
class MyClientHttpResponseDecorator extends ClientHttpResponseDecorator {
private final HttpHeaders httpHeaders;
public MyClientHttpResponseDecorator(ClientHttpResponse delegate) {
super(delegate);
this.httpHeaders = new HttpHeaders(this.getDelegate().getHeaders());
// mutate the content-type header when necessary
}
#Override
public HttpHeaders getHeaders() {
return this.httpHeaders;
}
}
Note that you should only use that client in that context (for this host).
I'd strongly suggest to try and fix that strange content-type returned by the server, if you can.
I'm looking for equivalent class RetrofitError in retrofit 2.0
I do sync call and need status code, headers, body, etc. For async call it is easy but how to do it with async?
My code:
try {
Response<User> execute = call.execute();
} catch (RetroFitError e)
{
call.getResponse(); //Response object containing status code, headers, body, etc.
}