Retrofit Documentation says:
"By default, Retrofit can only deserialize HTTP bodies into OkHttp's ResponseBody...Converters can be added to support other types"
This implies I should be able to make a api call WIHTOUT using the GSON converter, and get my response in the form of a "ResponseBody" object.
but I still get error
java.lang.IllegalArgumentException: Unable to create converter for class com.squareup.okhttp.ResponseBody
here is my code
#GET("v1/search")
Call<ResponseBody> getArtists(#Query("q") String name, #Query("type") String searchType);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.spotify.com/")
.build();
api = retrofit.create(SpotifyApi.class);
api.getArtists(searchBox.getText().toString(), "artist")
.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
Basically for I want to be able to use Retrofit in its purest/simplest form and just get a basic/raw response back. this is not for a real app, it's for experimentation.
You need to use okhttp3.ResponseBody from OkHttp 3.x (which Retrofit depends on). That error message indicates you are using the type from OkHttp 2.x.
Related
I'm using the Java SDK to start a voice call using something similar to
Call.creator(to, from, callbackAddress)
I provide a URL (callbackAddress) that will receive the callback once the call is connected. Is there some way to configure this callback to be in JSON instead of "application/x-www-form-urlencoded; charset=UTF-8"?
The reason why I'm trying to do that is because I'm using Spring and ultimately I'm trying to receive the request parameters already in a deserialized Pojo in my RestController (parameter body in my example below), which is standard in SpringMVC. This is much easier to do using jackson, which requires a JSON request body
As a secondary question, is there a class in the Twilio SDK that encapsulates all the parameters in a request already or I would have to create such class?
Here is a dummy rest controller to illustrate what I'm trying to do. Note that the logic there with the "out of city" error is just a silly demo to show why I need to access the request parameters. All the samples I found about callbacks always ignored the request parameters and returned a static TwiML
#RestController
#RequestMapping(value = "/twilio", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class TwilioCallbackController {
#PostMapping
public String handleCallback(RequestBody body /*this arg should have all request params*/) {
log.info("received callback for callId {}", body.getCallSid())
if (!body.toCity().equals("my-city")) {
throw new Exception("outside of city");
}
return createTwiML(body);
}
}
Twilio developer evangelist here.
There is no way to have Twilio send you the webhook in JSON format, it will be sent as form encoded parameters. However, there shouldn't be an issue having Spring parse them.
As this answer suggests, you can create a class that will parse the parameters into it by creating a class with getters and setters for each of the parameters.
So, for example, you could create the following class:
public class TwilioWebhook {
private String CallSid;
private String From;
public String getCallSid() {
return CallSid;
}
public void setText(String CallSid) {
this.CallSid = CallSid;
}
}
Which you could then use to parse the CallSid from the incoming webhook parameters like:
#RestController
#RequestMapping(value = "/twilio", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public class TwilioCallbackController {
#PostMapping
public String handleCallback(TwilioWebhook request) {
log.info("received callback for callId {}", request.getCallSid())
// rest of the controller.
}
}
You can parse all the parameters by adding to the TwilioWebhook class. You can see all the parameters that Twilio will send in the Twilio voice request documentation. There isn't a class in the Twilio SDK that does this for you though.
Been tackling this for two days. I'm trying to use the ProtoConverterFactory with gRPC but not having any luck with it.
public class RetrofitService {
public void makeRequest() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://myurl:444")
.addConverterFactory(ProtoConverterFactory.create())
.build();
RetrofitServiceImp serviceImp = retrofit.create(RetrofitServiceImp.class);
serviceImp.getToken(Empty.getDefaultInstance()).enqueue(new Callback<Token.TokenResponse>() {
#Override
public void onResponse(Call<Token.TokenResponse> call, Response<Token.TokenResponse> response) {
Log.d("SUCCESS", "onResponse: ");
}
#Override
public void onFailure(Call<Token.TokenResponse> call, Throwable t) {
Log.d("ERROR", "onFailure: " + t.getLocalizedMessage() + t.getMessage());
}
});
}
interface RetrofitServiceImp {
#POST("/")
Call<Token.TokenResponse> getToken(#Body Empty empty);
}
}
There are no examples of how to do this correctly so I tried following the Unit Tests in the converter factory.
https://github.com/square/retrofit/blob/master/retrofit-converters/protobuf/src/test/java/retrofit2/converter/protobuf/ProtoConverterFactoryTest.java
How do I link the compiled gRPC code to the converter or the retrofit lib?
The problem is that retrofit doesn't support gRPC out of the box.
Haven't tried it myself, but you might want to check Armenia framework. It supports gRPC and provides network engine compatible with retrofit's one.
https://line.github.io/armeria/client-retrofit.html
For future readers!
If you want to use gRPC with your android app instead of REST, use Square's Wire library.
Repo: https://github.com/square/wire
Website: https://square.github.io/wire/
I am working with retrofit and need to be able to use multiple interceptors. Currently I am using one to automatically append an auth token but i need to be able to make calls with no auth token. If i add another interceptor with no auth token in the header how do I use that one instead of the auth token interceptor.
val interceptor: Interceptor = Interceptor { chain ->
val newRequest = chain.request().newBuilder().
addHeader("Auth_Token", pref.getString(PSPreferences.prefAuthKey, "")).
cacheControl(CacheControl.FORCE_NETWORK).
build()
chain.proceed(newRequest)
}
okHttpClient = OkHttpClient.Builder().
readTimeout(1, TimeUnit.MINUTES).
connectTimeout(1, TimeUnit.MINUTES).
addInterceptor(interceptor).build()
val retrofitInstance = Retrofit.Builder()
.baseUrl(APIEndpointInterface.BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiInterface = retrofitInstance.create<APIEndpointInterface>(APIEndpointInterface::class.java)
OkHttpClient maintains a list of the interceptors which you can access, however it is an unmodifiable collection.
This leaves us with three options I believe:
Create two OkHttpClient instances, and by deduction two Retrofit
instances, one for the unauthenticated requests, and one for the
authenticated requests.
Check if you should use the interceptor, e.g. in your authentication interceptor, you can first check if there exists a key in your preferences for the token, and if so use it; if not, you simply proceed without modifying anything. You do this for your unauthenticated interceptor too. I think this is the easiest solution for your case.
Create a single interceptor, which will maintain a modifiable list
of interceptors which you can add and remove at will. You would need
to keep a reference to this interceptor, maybe make it a Singleton.
For the third option, I have provided a very simple example:
public class HttpRequestResponseInterceptor implements Interceptor {
public final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
public final List<ResponseInterceptor> responseInterceptors = new ArrayList<>();
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
for (RequestInterceptor interceptor : requestInterceptors) {
request = interceptor.intercept(request);
}
Response response = chain.proceed(request);
for (ResponseInterceptor interceptor : responseInterceptors) {
response = interceptor.intercept(response);
}
return response;
}
public interface RequestInterceptor {
Request intercept(Request request) throws IOException;
}
public interface ResponseInterceptor {
Response intercept(Response response) throws IOException;
}
}
In this case you would need to implement the custom interfaces RequestInterceptor and ResponseInterceptor.
An example of what an implementation of these interfaces would look like:
public class ExampleInterceptor implements HttpRequestResponseInterceptor.RequestInterceptor,
HttpRequestResponseInterceptor.ResponseInterceptor {
#Override
public Request intercept(Request request) throws IOException {
return request.newBuilder().addHeader("REQUEST_HEADER", "EXAMPLE").build();
}
#Override
public Response intercept(Response response) throws IOException {
return response.newBuilder().addHeader("RESPONSE_HEADER", "EXAMPLE").build();
}
}
You would then need to add this interceptor to our main interceptor twice, once to requestInterceptors and once to responseInterceptors (or only to one of these if it intercepts only requests or only responses).
This example is far from complete. The benefit of this solution is that it adds the ability to add and remove interceptors without having to recreate the OkHttpClient instance. It requires extra work if you want to support retrying requests, for example.
I am trying to learn about Retrofit since it seems to take care of a lot of the issues I am currently having with JSON requests and handling.
first and foremost, I understand that the methods we use are defined inside of interfaces, while making simple requests to obtain data it is quite simple to specify what is to be retrieved from the url as well as all the necessary endpoints based on the famous github example.
So if we are retrieving information form the github api, we would first create all the necessary pojo models and such and then define the interface as:
public interface GithubService {
#GET("users/{username}")
Observable<Github>getGithHubUser(#Path("username")String userName);
}
From that on the main activity we would have something like:
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.github.com/")
.build();
GithubService githubService = retrofit.create(GithubService.class);
Observable<Github> githubUser = githubService.getGithHubUser("usersName");
githubUser.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(user -> "Github Username: " + user.getName() + "\nUrl:" +user.getUrl() + "\nfollowing: "+ user.getHireable())
.subscribe(userInfo -> Log.d("Output", userInfo));
My question here would be how to send JSON information if the url requires something like this:
"data={\"process\":\"procesNumber\", \"phone\":\"123456\"}"
Basically, in order to get any response form the server I have been doing this using simple okhttp:
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(CREATE_MEDIA_TYPE, "data={\"process\":\"procesNumber\", \"phone\":\"123456\"}");
String ALLWAYS_API = "http://something something bla bla";
Request request = new Request.Builder()
.url("https://blablabla")
.post(body)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
... etc etc etc
}
To my understanding, even I need to create a pojo class that represents the data that needs to be sent to retrofit, something along the lines of:
public class DataRequest {
final String proces;
final String phone;
DataRequest(String process, String phone) {
this.process = process;
this.phone = phone;
}
}
Which would comply to the information being sent to the request, but how would I actually parse that to the interface implementation?
interface DataService {
#Post(not a clue what to place here)
DataRequest postJson(#Body how?)
}
And how would I actually add that to the retrofit builder? The examples that I am using come from different forums on the web as well as other questions asked by other users, this one in particular helped a lot in understanding a couple of things: How to POST raw whole JSON in the body of a Retrofit request? but I still don't understand where everything goes and some of the other questions and examples are far too complex for what I need to do.
Ok, so in order to leave an answer here for anyone trying to do this. By default, retrofit comes with many utilities which handle the passing of data as JSON, but in this case what I am passing is a string that looks like json inside of a tag called data......I know..
But in order to answer this for the people facing similar issues, in order to pass in the string we need to import a scalar convertor much in the same way that we need to import a gson converter to work with our retrofit services:
compile 'com.squareup.retrofit2:converter-scalars:2.0.2'
After that, our service can be handled as:
public interface CreateService {
#Headers({ "Content-Type: application/x-www-form-urlencoded;charset=UTF-8"})
#POST("your/post/path/goes/here")
Call<String> getStringScalar(#Body String body);
}
I write my service generators in a separate file, in this case, the whole thing can be used in this way:
public class ServiceGeneratorWithScalarConvertor {
private static final String API_BASE_URL = "your/base/url/goes/here";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
// basically, this code is the same as the one from before with the added instance of creating and making use of the scalar converter factory.....scratch that i took it off
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create());
public static <S> S createService(Class<S> serviceClass) {
builder.client(httpClient.build());
Retrofit retrofit = builder.build();
return retrofit.create(serviceClass);
}
}
From there, we can access the results with this particular method(i am using this method inside my main activity:
public void retroFitCreateAPIExample() {
CreateService service = ServiceGeneratorWithScalarConvertor.createService(CreateService.class);
String body = "data={\"process\":\"process1\",\"phone\":\"12345\"}";
Call<String> call = service.getStringScalar(body);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Log.d("Response Body>>>>>", response.body());
createR = new Gson().fromJson(response.body().toString(), CreateModels.class);
Log.d("CREATED RESPONSE",createR.getCreate().getStops().get(0).getCity());
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
}
});
}
The instance is this passed to the service generator that uses scalar convertors, the body of the post request is saved as a simple string(as it was specified in the interface) and we can do with the response whatever we want.
I am planning to make one common service class with the use of Retrofit,
#GET
Call<ResponseBody> makeGetRequest(#Url String url);
#POST
Call<ResponseBody> makePostRequest(#Url String url, #Body RequestBody parameters);
In this code i need to pass (ResponseBody) as a dynamic JSON POJO class name , Like LoginRes
Say for Example ,
Call<LoginRes> // But this class will be dynamic
I will pass ResponseBody but that ResponseBody does not know which class i wanted to prefer.
why i want this because , after result
gson.fromJson(response, LoginRes.class);
so, after getting result from Retrofit we again need to convert to gson.fromJson.
so i wanted to pass dynamic Response as Retrofit so that it will response according to my pojo class,
I know this is working fine when i pass LoginRes instead of ResponseBody because as i already told to Response that we need that response in LoginRes.
So if i pass
Call<LoginRes> // if i pass this way its working fine no need to convert my response i can access my all properties from that LoginRes class directly.
This is my example to call a Web service.
Call<ResponseBody> call = apiService.makePostRequest("/Buyer/LoginApp", requestBody);
This is how i call the Service.
Let me know if i am unclear with explanation of my problem.
waiting for some good response and suggestions on this.
Thanks
Madhav
This is a bit tricky but you'll need to use a custom Retrofit Converter Factory with a custom GsonBuilder which uses a custom JsonDeserializer.
Furthermore you should define an interface (CustomResonse in my Example) for which the CustomJsonDeserializer is used. This is not needed, but otherwise the Deserializer gets used for every request.
public class CustomConverterFactory {
public static GsonConverterFactory create() {
return GsonConverterFactory.create(createGsonCustomDeserializer());
}
public static Gson createGsonCustomJsonDeserializer() {
return new GsonBuilder()
.registerTypeAdapter(CustomResponse.class, new CustomJsonDeserializer())
.serializeNulls()
.create();
}
}
And for the Deserializer:
public class CustomJsonDeserializer implements JsonDeserializer<CustomResponse> {
#Override
public CustomResponse deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException {
if (json.isJsonObject()) {
JsonObject jsonObject = json.getAsJsonObject();
// Here you have to distinguish your class somehow
// Maybe read some Json field from your response
if (jsonObject.has("name")) {
JsonElement classes = jsonObject.get("name");
...
return context.deserialize(json, MyName.class);
}
...
// Default fallback: Deserialize as a fallback object
return context.deserialize(json, MyFallback.class);
} else {
throw new IllegalStateException();
}
}
Handle GET,POST or OTHER method Like this,
if (methodType.equalsIgnoreCase(CommonConfig.WsMethodType.GET)) {
apicall = getClient(CommonConfig.WsPrefix).create(ApiInterface.class).makeGetRequest(url + CommonConfig.getQueryString(new Gson().toJson(requestBody)), getAllHeader);
} else if (methodType.equalsIgnoreCase(CommonConfig.WsMethodType.POST)) {
apicall = getClient(CommonConfig.WsPrefix).create(ApiInterface.class).makePostRequest(url, RequestBody.create(MediaType.parse("application/json"), new Gson().toJson(requestBody)), getAllHeader);
}
Handle Response Like this.
apicall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
}
Retrofit Code
private Retrofit getClient(String WsPrefix) {
//TODO 60 to 30 second at everywhere
OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(WsPrefix)
.client(okHttpClient)
.build();
return retrofit;
}
Common Interface
interface ApiInterface {
#GET
Call<ResponseBody> makeGetRequest(#Url String url, #HeaderMap() Map<String, String> header);
#POST
Call<ResponseBody> makePostRequest(#Url String url, #Body RequestBody requestBody, #HeaderMap() Map<String, String> header);
}
ApiCallback
public interface ApiCallback {
void success(String responseData);
void failure(String responseData);
}