Android Retrofit update HTTP client configuration - android

We have a case where we may have to update the public keys when using Certificate Pinning with OKHttp client and Retrofit. My question is how I would update the certificate pinner of the http client after retrofit has been initialized (like, when a new public key has been received)?
Do I update the CertificatePinner in the http client and then create a new instance of retrofit? Or is there an easier way?
Any suggestions appreciated.
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.certificatePinner(NetworkUtils.getCertificatePinner()) ;
OKHTTPClient client = clientBuilder.build();
Retrofit myRetrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.build();
// Now I need to update the certificate pinner, like this?
client.certificatePinner(NetworkUtils.getCertificatePinner());
myRetrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.build();

Did you try using an interceptor. Something on the lines of inner class ExpiredSessionInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
if (response.code() == 202) {
val newRequest = request.newBuilder().build()
return chain.proceed(newRequest)
} else {
return response;
}
}
}

Related

How can i fix POST methods issue in redirect URLs for APIs in android and retrofit?

I am using some url for my apis which redirect to other one but i have some problem about POST methods ..
It gives me this message in the log:
405 Method Not Allowed
I am using retrofit in my app ..
How can i fix it to accept POST methods also?
This is my code:
public static Retrofit getClient() {
pref = getContext().getSharedPreferences(getContext().getPackageName(), 0);
languageSetting = pref.getString("languageSetting", null);
if (retrofit == null) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.MINUTES)
.readTimeout(5, TimeUnit.MINUTES);
httpClient.addInterceptor(logging);
// httpClient.followRedirects(false);
httpClient.addInterceptor(chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Accept", "application/json")
.header("Lang", languageSetting != null ? languageSetting : "EN")
.header("TokenKey", Constants.TOKEN)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
);
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient.build())
.build();
}
return retrofit;
}
I have tried it on post man and same issue but when i activate this option, it works fine :
The question now how to activate Follow Original HTTP method in android & Retrofit?

adding a header to all requests in the retrofit factory?

i have this factory which is used for all outgoing requests in the app, is it possible to add a header here (app version) instead of on all requests?
other examples ive seen all seem to use a different syntax for the factory, i think its an older one but i am not sure
object RetrofitFactory {
val makeRetrofitService: RetrofitService by lazy {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
Retrofit.Builder()
.baseUrl("${CustomURI.BaseWebsite}/")
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.client(client)
.build().create(RetrofitService::class.java)
}
}
You can add multiple interceptors to your OkHttpClient.
It should something like this:
This is your logging interceptor:
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
This is a header one
OkHttpClient.Builder().apply {
addInterceptor { chain ->
val request = chain.request()
val builder = request
.newBuilder()
.header("SOME", "SOME")
.method(request.method(), request.body())
val mutatedRequest = builder.build()
val response = chain.proceed(mutatedRequest)
response
}
addInterceptor(interceptor) // this is your http logging
}.build()
Change SOME and SOME with your preferred values.
I found this solution , you can add this by using Interceptor
in this link How to define a Header to all request using Retrofit?
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("User-Agent", "Retrofit-Sample-App");
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setRequestInterceptor(requestInterceptor)
.build();

Retrofit2 send Bearer Token

I've tried sending the token with a HeaderMap but get a 401 code response. The way my project is setup is that I have a separate file for my ApiClient and I have a OkHttpClient Interceptor and a HttpLoggingInterceptor to see whats going on, however I can't get the Bearer Token to work. I've seen solutions that add it to the interceptor as a header in the interceptor and I've tried this but since my token is saved in SharedPreferences I can't get it to work in the ApiClient class I have.
This is the ApiClient
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Gson gson = new GsonBuilder().serializeNulls().setLenient().create();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#NotNull
#Override
public okhttp3.Response intercept(#NotNull Chain chain) throws IOException {
Request originalRequest = chain.request();
Request newRequest = originalRequest.newBuilder()
//I would add the header here
//I tried this but it says on "ApiClient.this" cannot be referenced from static context
// .header("Authorization" , SharedPreferencesHelper.getUserToken(ApiClient.this));
.build();
return chain.proceed(newRequest);
}
})
.addInterceptor(interceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.6:8000/api/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
This is the method from SharedPreferencesHelper.getUserToken(MainActivity.this)
public static String getUserToken(Context context) {
SharedPreferences sp = getSharedPreferences(context);
return sp.getString(USER_TOKEN, null);
}
This is the current call where the response is 401, If I don't add the Accept => application/json the response url is incorrect and also returns a html page when I need a simple response return response("LoggedOut", 200); //this is the response in the api
Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
headers.put("Token", SharedPreferencesHelper.getUserToken(MainActivity.this));
Call<Void> call = apiInterface.LogoutUser(headers);
call.enqueue(new Callback<Void>() {
// onResponse and onFailure here
}
For example without the Accept header this is the response in the Logcat
D/OkHttp: --> GET http://192.168.0.6:8000/api/logout
D/OkHttp: Token: wE1Y8IxJpwyXtvw0fYoXZAlQ6qCx24YtzonQIeJBQSHmNppe0Sn1kLYDgZKCw4MKbpab4Vspf61Nzer1
D/OkHttp: --> END GET
D/OkHttp: <-- 200 OK http://192.168.0.6:8000/login
//a bunch of html that's the web page at this route, notice the /api is missing
How can I send this correctly?
EDIT:
I"m using a Laravel project for the backend and this is the relevant route
Route::middleware('auth:sanctum')
->get('/logoutApi', function (Request $request) {
$request->user()->tokens()->delete();
return response("LoggedOut", 202);
});
create class Authenticator, like:
const val HEADER_TOKEN_FIELD = "Authorization"
class ClassAuthenticator(
private val pref: SharedPref
) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
return response.request().newBuilder()
.header(HEADER_TOKEN_FIELD, pref.getToken())
.build()
}
}
then add interceptor in your client with:
val httpClient = OkHttpClient.Builder()
.authenticator(ClassAuthenticator(pref))
.addInterceptor { chain ->
val request = chain.request()
val httpRequest = request.newBuilder()
.addHeader(HEADER_TOKEN_FIELD,
"Bearer ${pref.getToken()}")
.build()
val response = chain.proceed(httpRequest)
response
}
.build()

Retrofit with OkHTTP not set Content-Type with #FormUrlEncoded

I'm trying to implement auth via x-www-form-urlencoded with Retrofit 2 on Android but faced a problem, that Header Content-Type not set with #FormUrlEncoded annotation, as well as I'm trying to set it manually, but when I'm setting it with a typo like Cotent-Type it works correctly and I can see it in headers.
Retrofit version: 2.4.0
So my question: why #FormUrlEncoded not set a content type as well as #Header annotation or what can remove it from headers.
My request:
#FormUrlEncoded
#POST("account/login")
Single<LoginResponse> login(#Field("memberId") String memberId,
#Field("pin") String pin);
OkHTTP/Retrofit provider with interceptors:
#Singleton
#Provides
Retrofit provideRetrofit(final OkHttpClient client, final Moshi moshi) {
return new Retrofit.Builder()
.baseUrl(Configuration.BASE_URL)
.client(client)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
#Provides
OkHttpClient provideOkHttpClient(#AppContext final Context context) {
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.followRedirects(true)
.followSslRedirects(true)
.addInterceptor(createLanguageInterceptor(context));
if (BuildConfig.DEBUG) {
builder.addInterceptor(new LoggingInterceptor());
}
return builder.build();
}
Interceptor createLanguageInterceptor(#AppContext final Context context) {
Locale current = context.getResources().getConfiguration().locale;
return chain -> {
Request.Builder builder = chain.request().newBuilder();
builder.addHeader("Accept-Language", current.getLanguage());
Request request = builder.build();
Response response = chain.proceed(request);
return response;
};
}
As a workaround, I've implemented the following interceptor:
Interceptor createHeaderTransformationInterceptor() {
return chain -> {
final Request request = chain.request();
String dataType = request.header("Data-Type");
final Request resultRequest = dataType == null
? request
: chain.request().newBuilder()
.removeHeader("Data-Type")
.addHeader("Content-Type", dataType)
.build();
return chain.proceed(resultRequest);
};
}
and it works fine with the following annotation:
#Headers({"Data-Type: application/x-www-form-urlencoded"})
UPD: the reason that my interceptor didn't see that is in a place where the content type is stored. The right way to see that header in an interceptor:
if (requestBody.contentType() != null) {
logger.log("Content-Type: " + requestBody.contentType());
}
if (requestBody.contentLength() != -1) {
logger.log("Content-Length: " + requestBody.contentLength());
}
By this Request
#FormUrlEncoded
#POST("account/login")
Single<LoginResponse> login(#Field("memberId") String memberId,
#Field("pin") String pin);
method #POST and #FormUrlEncoded automatic add
Content-Type: application/x-www-form-urlencoded in header you can check in log by
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor.setLevel(HttpLoggingInterceptor.Level.BODY))
.connectTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.build();
it print all log in verbose mode

Retrofit interceptor error

I want to add facebook access token into retrofit (2 beta 3) request, but the access token does not added.
I can add interceptor to retrofit 1.9 successfully but in retrofit 2 it has error, Is there any solution?
protected Retrofit getRestAdapter() {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
String sessionId = getSessionId(); // get access token
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Cookie", sessionId)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
})
.build();
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl("http://tbkha.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
return retrofit;
}
In retrofit retrofit 2.0 you add intercepter like this:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.client(client)
.build();
Check this link for details https://futurestud.io/blog/retrofit-2-log-requests-and-responses
Similar questions:
App crash on HttpLoggingInterceptor
Retrofit2 HttpLoggingInterceptor Logcat

Categories

Resources