Send empty body in POST request in Retrofit - java

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())

Related

Retrofit2 responseBodyConverter: I need request too

In a not so exotic situation I need access to the original request when I handle the response in the responseBodyConverter. For example, right now I need the original url -- e.g. I want to adjust relative urls in the response HTML. And perhaps I will need some request header, like a cookie.
But I don't know how to get the request data.
Here is what I have:
HttpLoggingInterceptor hlog = ses.http_logger(servername);
if (hlog != null) {
builder.addInterceptor(hlog);
}
OkHttpClient okhttpClient = builder
.build();
return new Retrofit.Builder()
.baseUrl(base_url)
.client(okhttpClient)
.addConverterFactory(
new Converter.Factory() {
#Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
return responseBody -> {
BufferedSource buffer = Okio.buffer(responseBody.source());
String html = buffer.readUtf8();
System.out.println(html);
};
}
})
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
;
I found this solution https://github.com/square/retrofit/issues/2267/
But I can not understand what it does or whether it is too complex or any side effects.
Can I get access to the request data in Retrofit2 while processing the response in responseBodyConverter and how?
Edit: the above works as expected, but a simpler way is preferred.

Using BodyInserters to pass parameter with webClient

There is my code.
public Mono<RespDto> function(TransReqDto1 reqDto1, TransReqDto2 reqDto2, String token) {
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("TransReqDto1", reqDto1);
builder.part("TransReqDto2", reqDto2);
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
LinkedMultiValueMap map = new LinkedMultiValueMap();
map.add("TransReqDto1", reqDto1);
map.add("TransReqDto2", reqDto2);
return
client.post()
.uri("/api")
.body(BodyInserters.fromValue(reqDto1))
.headers(h -> h.setBearerAuth(token.split(" ")[1]))
.retrieve()
.bodyToMono(RespDto.class);
}
My probelm is that I need to send both reqDto1 & reqDto2. I've successfully sent reqDto1 with the code above but I can't figure out a way to send two objects.
Tried MultipartBodybuild and MultiValueMap but both are returning error from the target API. Please give me some hints!! Thank you
Here is the API I am trying to call!
#PostMapping("")
#ApiOperation(value = "test", notes = "test")
public Mono<?> transPost(#Valid #RequestBody TransReqDto1 reqDto1,
#Valid #RequestBody TransReqDto2 reqDto2) {
return testService.function(reqDto1, reqDto2);
}
You cannot use two #RequestBody. It can bind to a single object only. The expected way to do that is to create a wrapper DTO containing all the relevant data:
public class TransReqDto {
private TransReqDto1 transReqDto1;
private TransReqDto2 transReqDto2;
//...
}

How to add interceptor to all API requests except one or two?

I know it's possible to add an interceptor to all requests through an OkHttpClient, but I would like to know if it's possible to add headers to all requests in Okhttp except for one request or two using the OkHttpClient.
For example, in my API all requests require a bearer token (Authorization: Bearer token-here header) except for the oauth/token (to get a token) and api/users (to register a user) routes. Is it possible to add an interceptor for all requests except the excluded ones using the OkHttpClient in one step or should I add the headers individually for each request?
I found the answer!
Basically I needed an interceptor as usual and I needed to check the URL there to know whether I should add the authorization header or not.
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* Created by Omar on 4/17/2017.
*/
public class NetInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (request.url().encodedPath().equalsIgnoreCase("/oauth/token")
|| (request.url().encodedPath().equalsIgnoreCase("/api/v1/users") && request.method().equalsIgnoreCase("post"))) {
return chain.proceed(request);
}
Request newRequest = request.newBuilder()
.addHeader("Authorization", "Bearer token-here")
.build();
Response response = chain.proceed(newRequest);
return response;
}
}
#Omar answer is Good :) but I found a more clean way to implement using custom annotation.
Add annotation
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
private annotation class DECRYPTRESPONSE
check if the annotation is true or false in an intercepter like this
val method = chain.request().tag(Invocation::class.java)!!.method()
if(method.isAnnotationPresent(DECRYPTRESPONSE::class.java)) {
//when annotion is present
} else..
add an annotation in retrofit interface
#DECRYPTRESPONSE
#GET
Call<ItemsModel> getListing(#Url String url);
below is the complete code of my interceptor also don't forget to add intercepter in the Okhttpclient builder
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
private annotation class DECRYPTRESPONSE
class DecryptInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain
.run {
proceed(request())
}
.let { response ->
return#let if (response.isSuccessful) {
val body = response.body!!
val contentType = body.contentType()
val charset = contentType?.charset() ?: Charset.defaultCharset()
val buffer = body.source().apply { request(Long.MAX_VALUE) }.buffer()
val bodyContent = buffer.clone().readString(charset)
val method = chain.request().tag(Invocation::class.java)!!.method()
if(method.isAnnotationPresent(DECRYPTRESPONSE::class.java)) {
response.newBuilder()
.body(ResponseBody.create(contentType, bodyContent.let(::decryptBody)))
.build()
}
else{
response.newBuilder()
.body(ResponseBody.create(contentType, bodyContent))
.build()
}
} else response
}
private fun decryptBody(content: String): String {
return content //your decryption code
}
}

Retrofit: Unable to create #Body converter for class

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,
)

How to add query parameters to a HTTP GET request by OkHttp?

I am using the latest okhttp version: okhttp-2.3.0.jar
How to add query parameters to GET request in okhttp in java ?
I found a related question about android, but no answer here!
For okhttp3:
private static final OkHttpClient client = new OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
public static void get(String url, Map<String,String>params, Callback responseCallback) {
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
if (params != null) {
for(Map.Entry<String, String> param : params.entrySet()) {
httpBuilder.addQueryParameter(param.getKey(),param.getValue());
}
}
Request request = new Request.Builder().url(httpBuilder.build()).build();
client.newCall(request).enqueue(responseCallback);
}
Here's my interceptor
private static class AuthInterceptor implements Interceptor {
private String mApiKey;
public AuthInterceptor(String apiKey) {
mApiKey = apiKey;
}
#Override
public Response intercept(Chain chain) throws IOException {
HttpUrl url = chain.request().url()
.newBuilder()
.addQueryParameter("api_key", mApiKey)
.build();
Request request = chain.request().newBuilder().url(url).build();
return chain.proceed(request);
}
}
I finally did my code, hope the following code can help you guys. I build the URL first using
HttpUrl httpUrl = new HttpUrl.Builder()
Then pass the URL to Request requesthttp hope it helps .
public class NetActions {
OkHttpClient client = new OkHttpClient();
public String getStudentById(String code) throws IOException, NullPointerException {
HttpUrl httpUrl = new HttpUrl.Builder()
.scheme("https")
.host("subdomain.apiweb.com")
.addPathSegment("api")
.addPathSegment("v1")
.addPathSegment("students")
.addPathSegment(code) // <- 8873 code passthru parameter on method
.addQueryParameter("auth_token", "71x23768234hgjwqguygqew")
// Each addPathSegment separated add a / symbol to the final url
// finally my Full URL is:
// https://subdomain.apiweb.com/api/v1/students/8873?auth_token=71x23768234hgjwqguygqew
.build();
System.out.println(httpUrl.toString());
Request requesthttp = new Request.Builder()
.addHeader("accept", "application/json")
.url(httpUrl) // <- Finally put httpUrl in here
.build();
Response response = client.newCall(requesthttp).execute();
return response.body().string();
}
}
As mentioned in the other answer, okhttp v2.4 offers new functionality that does make this possible.
See http://square.github.io/okhttp/2.x/okhttp/com/squareup/okhttp/HttpUrl.Builder.html#addQueryParameter-java.lang.String-java.lang.String-
This is not possible with the current version of okhttp, there is no method provided that will handle this for you.
The next best thing is building an url string or an URL object (found in java.net.URL) with the query included yourself, and pass that to the request builder of okhttp.
As you can see, the Request.Builder can take either a String or an URL.
Examples on how to build an url can be found at What is the idiomatic way to compose a URL or URI in Java?
As of right now (okhttp 2.4), HttpUrl.Builder now has methods addQueryParameter and addEncodedQueryParameter.
You can create a newBuilder from existing HttoUrl and add query parameters there. Sample interceptor code:
Request req = it.request()
return chain.proceed(
req.newBuilder()
.url(
req.url().newBuilder()
.addQueryParameter("v", "5.60")
.build());
.build());
Use HttpUrl class's functions:
//adds the pre-encoded query parameter to this URL's query string
addEncodedQueryParameter(String encodedName, String encodedValue)
//encodes the query parameter using UTF-8 and adds it to this URL's query string
addQueryParameter(String name, String value)
more detailed: https://stackoverflow.com/a/32146909/5247331

Categories

Resources