Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read plain text response from server using Retrofit

I'm working on an application that uses Retrofit for network operations. As it stands, everything works well with GsonConverterFactory handling serialization. Here is how I setup Retrofit

Retrofit.Builder()
        .baseUrl("<base url>") 
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()

Now I need to connect to a legacy service which returns content in text/plain; charset=utf-8 format. Here is the Retrofit interface

@GET("https://<domain>/<endpoint>?Type=Query")
suspend fun callStatus(@Query("userId") id: Int): Response<String>

This will return status of a call for a valid user. For instance, if the user is valid and there is a status, it returns "Active" as plain text. If there is no valid user, it returns an error code of #1005

I could add custom converter factory like this (found on the web)

final class StringConverterFactory implements Converter.Factory {
    private StringConverterFactory() {}

    public static StringConverterFactory create() {
        return new StringConverterFactory();
    }

    @Override
    public Converter<String> get(Type type) {
        Class<?> cls = (Class<?>) type;
        if (String.class.isAssignableFrom(cls)) {
            return new StringConverter();
        }
        return null;
    }

    private static class StringConverter implements Converter<String> {
        private static final MediaType PLAIN_TEXT = MediaType.parse("text/plain; charset=UTF-8");

        @Override
        public String fromBody(ResponseBody body) throws IOException {
            return new String(body.bytes());
        }

        @Override
        public RequestBody toBody(String value) {
            return RequestBody.create(PLAIN_TEXT, convertToBytes(value));
        }

        private static byte[] convertToBytes(String string) {
            try {
                return string.getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

But I didn't see it make any difference. Also, it could well disguise JSON as normal text and break all existing service. Is there a better way to handle this scenario? I thought of having separate retrofit instance for plain text, bit dirty though. Do you have any other suggestions/solutions?

Edited

Response header contains the content type as Content-Type: text/plain; charset=utf-8

Actual response for valid user

Active

Actual response for invalid user

#1005
like image 329
Renjith Avatar asked Dec 10 '22 00:12

Renjith


2 Answers

Update


The order in which you register the converter factories matters. ScalarsConverterFactory must come first.


it should be possible by adding ScalarsConverterFactory when building the Retrofit object.
This can be done alongside with other json converters, e.g.

Retrofit.Builder()
        .baseUrl("<base url>") 
        .client(client)
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()

After that, you should be able to receive plaintext responses.

You probably need to add this to your dependencies as well:

implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
like image 147
Adrian K Avatar answered Dec 26 '22 15:12

Adrian K


The following is the way that how I get response as plain text (using Java not Kotlin).

Step One

in your gradle (Module);

    implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'

Step Two

Create an interface

public interface MyInterface {
    @GET("something.php")
    Call<String> getData(@Query("id") String id,
                         @Query("name") String name);
}

Step Three

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://example.com")
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();

MyInterface myInterface = retrofit.create(MyInterface.class);
Call<String> call = myInterface.getData("id","myname");
call.enqueue(new Callback<String>() {
                    @Override
                    public void onResponse(Call<String> call, Response<String> response) {
                        String plain_text_response = response.body();
                    }

                    @Override
                    public void onFailure(Call<String> call, Throwable t) {

                    }
                });
like image 25
ZBorkala Avatar answered Dec 26 '22 14:12

ZBorkala