I need to send next json via retrofit 2:
{
"Inspection": {
"UUID": "name",
"ModifiedTime": "2016-03-09T01:13",
"CreatedTime": "2016-03-09T01:13",
"ReviewedWith": "name2",
"Type": 1,
"Project": {
"Id": 41
},
"ActionTypes": [1]
}
}
With Header: Authorization: access_token_value
I tried this:
//header parameter
String accessToken = Requests.getAccessToken();
JsonObject obj = new JsonObject();
JsonObject inspection = new JsonObject();
inspection.addProperty("UUID","name");
inspection.addProperty("ModifiedTime","2016-03-09T01:13");
inspection.addProperty("CreatedTime","2016-03-09T01:13");
inspection.addProperty("ReviewedWith","name2");
inspection.addProperty("Type","1");
JsonObject project = new JsonObject();
project.addProperty("Id", 41);
inspection.add("Project", project);
obj.add("Inspection", inspection);
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(Constants.ROOT_API_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.build();
IConstructSecureAPI service = restAdapter.create(IConstructSecureAPI.class);
Call<JsonElement> result = service.addInspection(accessToken, obj);
JsonElement element = result.execute().body();
But everytime i recieved exception: java.lang.IllegalArgumentException: Unable to create #Body converter for class com.google.gson.JsonObject (parameter #2)
How can I send it ? Or any another idea how I can do it. You can even offer me with parameter as simple String with json inside. It will suit for me
Solution:
declare body value in your interface with next:
#Body RequestBody body
and wrap String JSON object:
RequestBody body = RequestBody.create(MediaType.parse("application/json"), obj.toString());
There is chance of you kept same #SerializedName("") for multiple vairable/fields/tags
You can specify a Converter when you create the Retrofit like this
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseurl)
.client(okHttpClient)
.build();
If this is due to #SerializedName make sure it is not dulplicated.
For e.g. This error will be thrown in below case: (Note: bookingId is passed twice)
#SerializedName(value="bookingId", alternate={"id", "bookingId"})
But, this is the correct:
#SerializedName(value="bookingId", alternate={"id", "someOtherId", "whateverId"})
Body uses a single request object, declare your request object as following
class Inspection {
String UUID;
//..... add your fields
Project project;
}
class Product
{
int Id;
//....... add your fields
}
I assume your service IConstructSecureAPI endpoint is:
#GET(...) // change based on your api GET/POST
Call<Response> addInspection(
#Header("Authorization") String accesstoken,
#Body Inspection request
);
and you can declare your desire Response.
Check this answer, it uses HashMap instead of class.
I kept getting this error when I upgraded to Java 17, still working fine on Java 11.
Here's what worked for me
To go deeper into the Exception I put a debug point in the Utils.java found in retrofit stack trace.
Doing so led me to the narrower cause being: java.lang.reflect.InaccessibleObjectException: Unable to make field private final byte java.time.LocalTime.hour accessible: module java.base does not "opens java.time" to unnamed module #35e2d654
Googling down further from here led me to https://github.com/mockk/mockk/issues/681#issuecomment-959646598 which in a nutshell suggests to add --add-opens java.base/java.time=ALL-UNNAMED as a JVM argument.
Boom, It worked.
In my case I simply forgot to apply the kotlinx.serialization plugin for Gradle, so no code for the serializers was generated. Fix it via:
plugins {
kotlin("plugin.serialization")
}
You can use an Interceptor to send Authorization Header in each request
class AuthorizationInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String authorizationToken = AuthenticationUtils.getToken();
Request authorizedRequest = originalRequest.newBuilder()
.header("Authorization", authorizationToken)
.build();
return chain.proceed(authorizedRequest);
}
}
I am using Moshi and Retrofit and my problem was I forgot to add #JsonSerializable annotation for the DTO class for #body.
you should add this annotation like this:
#JsonSerializable
data class RegisterDTO(
#field:Json(name = "device_id") val deviceId: String,
)
Related
I am trying to add parameters to a postForEntity request but it seems to never go through. Here is the minimum reproducible code:
#Test
public void test()
{
String urlTemplate = UriComponentsBuilder.fromHttpUrl("http://localhost:8080/test")
.queryParam("update")
// .queryParam("update", "{update}") //This does not work either
.encode()
.toUriString();
HashMap<String, String> paramValues = new HashMap<>();
paramValues.put("update", "true");
HttpEntity<AnimateRequest> httpEntity = new HttpEntity<>(null, new HttpHeaders());
ResponseEntity<Boolean> response = this.template.postForEntity(
urlTemplate,
httpEntity,
Boolean.class,
paramValues);
boolean bb = response.getBody();
}
In a controller:
#PostMapping(value = "/test")
public ResponseEntity<Boolean> tester(#RequestParam(name="update", required = false) boolean rr)
{
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(rr);
}
Errors with:
org.springframework.web.client.RestClientException: Error while extracting response for type [class java.lang.Boolean] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1]
I'm not exactly sure why, but the return contentType() in the controller return needed to be removed. For some reason Jackson was not parsing the return type properly. Then either a primitive boolean or the class Boolean works.
Main problem, is that your implementation tries to respond with text/plain without registering any Boolean to text/plain converter.
You have several options to solve that:
Just return (response with) the "default (media) type":
return ResponseEntity
.ok()
.body(rr);
If you need to respond with text/plain, then
a. ResponseEntity<String> would be the straight-forward solution:
#PostMapping(value = "/test2")
public ResponseEntity<String> // String!!! not Boolean ... {
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_PLAIN) // explicit media type here or as #PostMapping.produces attribute
.body(String.valueOf(rr)); // convert the boolean here
}
b. Or to really register a custom(ized) (Boolean<->text/plain) converter ...
Then we could test 1. (with TestRestTemplate) like:
#Test
public void test1() throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort + "/test/";
URI uri = new URI(baseUrl);
// true:
ResponseEntity<Boolean> result = this.restTemplate.postForEntity(uri + "?update=true", null /*here goes normally the "post body"/entity*/, Boolean.class);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
assertThat(result.getBody()).isTrue();
}
and 2. accordingly with string result:
#Test
public void test2() throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort + "/test2/";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = this.restTemplate.postForEntity(uri + "?update=true", null, String.class);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
assertThat(Boolean.valueOf(result.getBody())).isTrue();
}
Please consider, we have several (out-of-the-box) options regarding "content encoding" and "how to pass this update parameter".
For brevity, simplicity and lack of need I omit any post objects and headers (null, which would go as the second method argument), and passed the only parameter as "URI parameter".
Also consider the note on RestTemplate, which could also be applied to TerstRestTemplate:
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.
In my Android app I connect through REST to Delphi application on Firebird server using Retrofit.
There is REST method "SelectSQL" which takes two parameters: String and TJSONObject.
For example:
select name from employee where employee_id=:id
{"id":10001}
This is part of interface where I declare methods: I used string and JSONObject.
#POST("datasnap/rest/TstBaseMethods/SelectSQL/{param,param2}")
Call<Logowanie> selectSQL(#Header("Authorization") String credentials, #Query("param") String param, #Query("param2") JSONObject param2 );
In my MainActivity.java I use:
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
StreamREST gerritAPI = retrofit.create(StreamREST.class);
String dane = Credentials.basic("admin","admin");
JSONObject obj = new JSONObject();
try {
obj.put("NAGL",11101);
String dd = obj.toString();
Call<Logowanie> sql = gerritAPI.selectSQL(dane,"select n.datadok from nagl n where n.id_nagl=:NAGL",obj);
sql.enqueue(new Callback<Logowanie>() {
#Override
public void onResponse(Call<Logowanie> call, Response<Logowanie> response) {
if(response.isSuccessful()) {
Logowanie log = response.body();
String result = log.result[0];
intent.putExtra(EXTRA_MESSAGE,generujWynik(log));
startActivity(intent);
} else {
System.out.println(response.toString());
}
}
#Override
public void onFailure(Call<Logowanie> call, Throwable t) {
System.out.println(t.getMessage() );
}
});
} catch (JSONException e) {
e.printStackTrace();
}
The problem is Retrofit doesn't properly encodes second parameter. When I launch app, I get message about bad second parameter and URL looks like that:
http://192.168.94.155:9000/datasnap/rest/TstBaseMethods/SelectSQL/%7Bparam,param2%7D?param=select%20n.datadok%20from%20nagl%20n%20where%20n.id_nagl%3D:NAGL¶m2={%22NAGL%22:11101}
You can see: "param2={%22NAGL%22:11101}" where param2 looks like:
{"NAGL":11101}
Brackets and "" aren't encoded. Where is my mistake? I admit these are my first steps with Android and REST with Stackoverflow as well, but I've already made basics: I launched basic REST method and it worked. Now is problem for me.
If I didn't include important parts of my code, just tell me and I will do it.
I can add that it's about DataSnap REST from Embarcadero.
Problem solved. It turned out that I had to use #Body Retrofit annotation as this parameter should go in body of the request.
My api expects an empty json body ({ }) when making post requests. How do I set this up in Retrofit and Jackson?
I tried passing null, and empty string, and "{}" but could not get this to work.
#POST(my/url)
Call<MyResponse> createPostRequest(#Body Object empty);
How can I set an empty JSON body?
An empty Object does it for Kotlin:
interface ApiService {
#POST("your.url")
fun createPostRequest(#Body body: Any = Object()): Call<YourResponseType>
}
try this . It worked for me now.
#POST(my/url)
Call<MyResponse> createPostRequest(#Body Hashmap );
while using this method pass new HasMap as paremater
apiservice.createPostRequest(new HashMap())
Empty class will do the trick:
class EmptyRequest {
public static final EmptyRequest INSTANCE = new EmptyRequest();
}
interface My Service {
#POST("my/url")
Call<MyResponse> createPostRequest(#Body EmptyRequest request);
}
myService.createPostRequest(EmptyRequest.INSTANCE);
Old question, but I found a more suitable solution by using a okhttp3.Interceptor that adds an empty body if no body is present. This solution does not require you to add an extra parameter for an empty #Body.
Example:
Interceptor interceptor = chain -> {
Request oldRequest = chain.request();
Request.Builder newRequest = chain.request().newBuilder();
if ("POST".equals(oldRequest.method()) && (oldRequest.body() == null || oldRequest.body().contentLength() <= 0)) {
newRequest.post(RequestBody.create(MediaType.parse("application/json"), "{}"));
}
return chain.proceed(newRequest.build());
};
You can then create an instance of your service like so:
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.addInterceptor(interceptor);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("YourURL")
.client(client.build())
.build();
MyService service = retrofit.create(MyService.class);
use:
#POST("something")
Call<MyResponse> createPostRequest(#Body Object o);
then call:
createPostRequest(new Object())
Heres the answer in Kotlin:
#POST("CountriesList")
fun getCountriesNew(#Body body: HashMap<String, String>) : Call<CountryModel>
val call = RetrofitClient.apiInterface.getCountriesNew(HashMap())
I would like to understand how Retrofit works, but the official documentation is very weak.
I need to make a very simple GET request and get the response as a String.
Now I use standard HTTPUrlConnection and it works nicely, just request - response
Can anyone tell me how to get a String response without converting it to an object or something like that?
You can use ScalarsConverterFactory for strings and both primitives and their boxed types to text/plain bodies.
Add this dependency to your build.gradle file:
compile 'com.squareup.retrofit2:converter-scalars:2.1.0'
Try this:
public interface ExampleService {
#GET("/users/{user}/repos")
Call<String> listRepos(#Path("user") String user);
}
And add ScalarsConverterFactory to your builder:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
You can then retrieve this string like this:
Call<String> call = exampleService.listRepos(user);
Response<String> response = call.execute();
String value = response.body();
I'm trying to pass a string of the format below as the body of a http post request.
param1=PARAM1¶m2=PARAM2¶m3=PARAM3
But retrofit encodes my body so that = becomes \u003d and & becomes \u0026. And I end up with a string which actually looks like this:
param1\u003dPARAM1\u0026param2\u003dPARAM2\u0026param3\u003dPARAM3
How can I prevent that?
My retrofit rest api is defined as follows.
public interface RestAPI {
#POST("/oauth/token")
public void getAccessToken(#Body String requestBody, Callback<Response> response);
}
If you have a serialized class (like a HashMap) in the request body and you want to prevent encoding that (like in vezikon's and my problem), you can create a custom Gson with disabled escaping using:
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
Pass this converter to your rest adapter:
yourRestAdapter = new RestAdapter.Builder()
.setEndpoint(.....)
.setClient(.....)
.setConverter(new GsonConverter(gson))
.build();
This way the "=" characters in the post body stay intact while submitting.
To answer the question directly, you can use TypedString as the method parameter type. The reason the value is being changed is because Retrofit is handing the String to Gson in order to encode as JSON. Using TypedString or any TypedOutput subclass will prevent this behavior, basically telling Retrofit you will handle creating the direct request body yourself.
However, that format of payload is called form URL encoding. Retrofit has native support for it. Your method declaration should actually look like this:
#FormUrlEncoded
#POST("/oauth/token")
void getAccessToken(
#Field("param1") String param1,
#Field("param2") String param2,
#Field("param3") String param3,
Callback<Response> callback);
Using Kotlin
For Retrofit 2 you can initialize retrofit with a Gson converter factory.
val builder = GsonBuilder().disableHtmlEscaping().create()
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(builder))
.client(monoOkHttpClient())
.build()
This builder should remove escaping from your json output.
Gradle file dependencies:
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
This issue can be fixed with below workaround.
#POST("yourString")
Call<YourResponseModel> yourCallMethod(#Query("yourKey") String yourValue,
#Query("yourKey") String yourValue,
#Query("yourKey") String yourValue);
Note : Don't use "#FormUrlEncoded" for this case.
Reference Here - https://github.com/square/retrofit/issues/1407