In my Android app I'm using Retrofit + OkHttp to pass a client token in my post header in order to return an access token. I'm having trouble figuring out why I'm getting a 405 response. The 405 response shows my url is correct. This is my api service:
public interface TokenApiService {
String clientToken = "############";
#POST("token")
Call<Auth> fetchAuth(#Header("Authorization") String clientToken, #Body Auth auth);
}
This is my module. I'm able to return GET requests, but not POST:
#Module
#InstallIn(SingletonComponent.class)
public class NetworkModule {
public NetworkModule() {
}
#Provides
#Singleton
public static TokenApiService provideTokenApi(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(URL.ACCESS_TOKEN_URL)
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.callbackExecutor(Executors.newSingleThreadExecutor()).client(okHttpClient)
.build().create(TokenApiService.class);
}
#Provides
#Singleton
public static ClientApiService provideClientApi(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(URL.SCHEDULE_BASE_URL)
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.callbackExecutor(Executors.newSingleThreadExecutor()).client(okHttpClient)
.build().create(ClientApiService.class);
}
#Provides
#Singleton
public static ApiService provideApi(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(URL.BASE_URL)
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.callbackExecutor(Executors.newSingleThreadExecutor()).client(okHttpClient)
.build().create(ApiService.class);
}
#Provides
#Singleton
public static OkHttpClient provideClient(Interceptor interceptor) {
Cache cache = new Cache(new File(Application.getInstance().getCacheDir(), "http-cache"), 10 * 1024 * 1024);
return new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.cache(cache)
.build();
}
#Provides
#Singleton
public static Interceptor provideInterceptor() {
return new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (ConnectivityUtil.isNetworkConnected()) {
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(2, TimeUnit.MINUTES)
.build();
return response.newBuilder()
.header("Cache-Control", cacheControl.toString())
.build();
} else {
Log.d("Paras", "Returning old");
return response;
}
}
};
}
}
Any help would be greatly appreciated.
Related
I want to get to the service in specific URL .... I use retrofit to get it but it give me an error connection to the URL...while the URL is active in chrome.
in the code I write ??? instead to correct one ^__^ (this code is class APIClient)
and to be sure that I work in correct way I was built an api in mocky site with the same json file .... and it is work
so can any one help me please
public static final String BASE_URL = "http://111.222.1.12:0000/???/???/????/";
//database
public static final String BASE_URL2 = "http://www.mocky.io/v2/";
public static Boolean URL=true;
private static Retrofit retrofit = null;
public static Retrofit getClient(){
if (retrofit==null) {
if(URL)
{
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
else
{
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL2)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
}
return retrofit;
}
Hello you can try using my ApiClient, I'm using rxJava so you need to redesign the APIClient to your need
public class ApiClient {
private static Retrofit retrofit = null;
private static int REQUEST_TIMEOUT = 120;
private static OkHttpClient okHttpClient;
public static Retrofit getClient(Context context) {
if (okHttpClient == null)
initOkHttp(context);
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(API_PATH)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
private static void initOkHttp(Context context) {
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
OkHttpClient.Builder httpClient = new OkHttpClient().newBuilder()
.connectTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(interceptor);
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Request-Type", "Android")
.addHeader("Content-Type", "application/json");
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
okHttpClient = httpClient.build();
}
public static void resetApiClient() {
retrofit = null;
okHttpClient = null;
}
}
Hope this reference can help you solve your problems
I want to use two server url using retrofit, but only one is working when I am using two base url. Please tell me how to use two base url in android.
public class APIUtils {
public static String Url1 = "http://10.0.13.46:19460";
public static String Url12 = "http://freshcamera.herokuapp.com";
public static SOService getSOService(String url) {
return RetrofitClient.getClient(url1).create(SOService.class);
}
}
SOService class
public interface SOService {
//URL 2
#FormUrlEncoded
#POST("/api/user/LoginUser")
Call<Login> Login(#Field("username") String username, #Field("password")String password, #Field("grant_type")String passwords);
}
SOService_AI class
public interface SOService_AI {
//URL 1
#FormUrlEncoded
#POST("/finalresult1")
Call<List<AIImageProcessing>> AiImageCheck(#Field("img_data") String imgdata, #Field("name")String imgName);
}
I guess what you need is changing URL at runtime to a completely different one.
For example, the following code will override the URL passed as baseUrl to retrofit object.
#GET
public Call<ResponseBody> profilePicture(#Url String url);
Note: You can't add url param to #GET and #POST. The URL must be passed to #Url.
// ERROR ( #Url cannot be used with #GET URL)
#GET("users") // or POST
public Call<Foo> getUsers(#Url String url);
// CORRECT
#GET
public Call<Foo> getUsers(#Url String fullUrl);
Checkout this tutorial for further information.
if you are working two url then you create two retrofit object. because single retrofit object work on single url.
if you want to access two your make two retofit object like below code..
public class ApiClient {
private final static String BASE_URL = "https://simplifiedcoding.net/demos/";
private final static String BASE_URL2 = "http://freshcamera.herokuapp.com";
public static ApiClient apiClient;
private Retrofit retrofit = null;
private Retrofit retrofit2=null;
public static ApiClient getInstance() {
if (apiClient == null) {
apiClient = new ApiClient();
}
return apiClient;
}
//private static Retrofit storeRetrofit = null;
public Retrofit getClient() {
return getClient(null);
}
public Retrofit getClient2() {
return getClient2(null);
}
private Retrofit getClient(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
private Retrofit getClient2(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL2)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
}
then after access like below code ..
ApiClient.getInstance().getClient();
ApiClient.getInstance().getClient2();
with Kotlin its even easier
companion object {
// init Retrofit base server instance
val redditClient by lazy { ApiService.invoke(REDDIT_BASE_URL) }
val stackClient by lazy { ApiService.invoke(STACK_BASE_URL) }
private val loggingInterceptor = HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BODY
}
operator fun invoke(baseUrl: String): ApiService {
val client = OkHttpClient.Builder().apply {
/**addNetworkInterceptor(StethoInterceptor()) */
addNetworkInterceptor(loggingInterceptor)
connectTimeout(10, TimeUnit.MINUTES)
readTimeout(10, TimeUnit.MINUTES)
writeTimeout(10, TimeUnit.MINUTES)
}.build()
return Retrofit.Builder()
.client(client)
.baseUrl(baseUrl)
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
just pass the baseUrl in the invoke method
This is really easy now
Simply use Post or Get without a constant url instead accept it in a parameter and annotate that parameter with #Url
#GET
suspend fun handshakeUser(#Url url : String): Response<JsonObject>
#POST
suspend fun makePostRequest(
#Header("Authorization") token: String = getToken(),
#Url url: String,
#Body inputModel: JsonObject
): Response<JsonObject>
I'm trying to change the baseUrl of an android app dynamically but the url doesn't changed.
I'm taking the reference of David's answer from here,the OkHttp Approach Dagger + Retrofit dynamic URL and Set dynamic base url using Retrofit 2.0 and Dagger 2, but still no luck.
Initially the app point to the urls "https://imd.com/yeddydemo/wpApp/",which is our app base url.
After doing some googling i have written the following code to change the app base url points to https://imd.com/rahudemo/wpApp/ but it doesn't works correctly:
Can anyone please point out where i'm doing wrong.Thanks in Advance:)
Method to change the base url:
public void changeUrlConfiguration(String name){
NetComponent netComponent = DaggerNetComponent.builder()
.apiModule(new ApiModule("https://imd.com/rahudemo/wpApp/"))
.appModule(new AppModule((ImdPoApp)homeActivity.getApplication()))
.storageModule(new StorageModule((ImdPoApp)homeActivity.getApplication()))
.build();
ApiStories service = netComponent.retrofit().create(ApiStories.class);
HostSelectionInterceptor interceptor = netComponent.interceptor();
interceptor.setHost("https://imd.com/rahudemo/wpApp/","8WAtp8nUEOrzSu67t9tGITEo");
}
Interceptor class
public final class HostSelectionInterceptor implements Interceptor {
private volatile String host;
private String authKey;
public void setHost(String host,String authKey) {
this.host = host;
this.authKey=authKey;
new ApiModule(host);
}
#Override public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String host = this.host;
if (host != null) {
HttpUrl newUrl = HttpUrl.parse(host);
request = request.newBuilder()
.url(newUrl)
.build();
}
return chain.proceed(request);
}
}
ApiModule
#Module
public class ApiModule {
String mBaseUrl;
HostSelectionInterceptor sInterceptor;
public ApiModule(String mBaseUrl) {
this.mBaseUrl = mBaseUrl;
}
#Provides
#Singleton
Cache provideHttpCache(Application application) {/*..*/}
#Provides
#Singleton
Gson provideGson() {/*..*/}
#Provides
#Singleton
OkHttpClient provideOkhttpClient(Cache cache, HostSelectionInterceptor hostSelectionInterceptor) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.addInterceptor(chain -> {
Request request = chain.request();
Request.Builder builder = request.newBuilder().addHeader("Authkey", "8WAtp8nUEOrzSu67t9tGITEo");
return chain.proceed(builder.build());
});
client.addInterceptor(hostSelectionInterceptor);
client.cache(cache);
return client.build();
}
#Provides
#Singleton
HostSelectionInterceptor provideInterceptor() {
if (sInterceptor == null) {
sInterceptor = new HostSelectionInterceptor();
}
return sInterceptor;
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
}
}
In your method pass the base url want to change to newbaseurl
public void changeUrlConfiguration(String name, String newbaseurl){
NetComponent netComponent = DaggerNetComponent.builder()
.apiModule(new ApiModule(newbaseurl))
.appModule(new
AppModule((ImdPoApp)homeActivity.getApplication()))
.storageModule(new StorageModule((ImdPoApp)homeActivity.getApplication()))
.build();
ApiStories service = netComponent.retrofit().create(A
piStories.class);
HostSelectionInterceptor interceptor = netComponent.interceptor();
interceptor.setHost(newbaseurl,"8WAtp8nUEOrzSu67t9tGITEo");
}
By Changing my interceptor & ChangeUrlConfiguration method as below i can able to change the BaseUrl
Interceptor method
#Override public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String host = this.host;
if (host != null) {
HttpUrl newUrl = request.url().newBuilder()
.removePathSegment(0).removePathSegment(0).removePathSegment(0).addPathSegments(host).addPathSegment("wpApp").addEncodedPathSegment("api.php")
.build();
request = request.newBuilder()
.url(newUrl).addHeader("Authkey", "8WAtp8nU")
.build();
} else {
request = request.newBuilder().addHeader("Authkey", "8WAtp8nU")
.build();
}
try {
return chain.proceed(request);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
changeUrlConfiguration
public void changeUrlConfiguration(String constituencyName){
String newbaseurl="https://imdstar.com/rahuldemo/wpApp/";
hostSelectionInterceptor.setHost(newbaseurl,"8WAtp8nUEOrzSu67t9tGITEzIdgr6huIpXqo");
}
i just learn about how can i implementing Retrofit with Dagger2 to set dynamic change url on this reference
i try to make simple module with HostSelectionInterceptor class to use that on Dagger2, but i can't make that correctly and i get error:
my NetworkModule:
#Module(includes = ContextModule.class)
public class NetworkModule {
#Provides
#AlachiqApplicationScope
public HttpLoggingInterceptor loggingInterceptor() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
Timber.e(message);
}
});
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
return interceptor;
}
...
#Provides
#AlachiqApplicationScope
public HostSelectionInterceptor hostSelectionInterceptor() {
return new HostSelectionInterceptor();
}
#Provides
#AlachiqApplicationScope
public OkHttpClient okHttpClient(HostSelectionInterceptor hostInterceptor, HttpLoggingInterceptor loggingInterceptor, Cache cache) {
return new OkHttpClient.Builder()
.addInterceptor(hostInterceptor)
.addInterceptor(loggingInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.cache(cache)
.build();
}
}
and HostSelectionInterceptor module:
#Module(includes = {NetworkModule.class})
public final class HostSelectionInterceptor implements Interceptor {
private volatile String host;
#Provides
#AlachiqApplicationScope
public String setHost(String host) {
this.host = host;
return this.host;
}
public String getHost() {
return host;
}
#Provides
#AlachiqApplicationScope
#Override
public okhttp3.Response intercept(Chain chain) {
Request request = chain.request();
String host = getHost();
if (host != null) {
HttpUrl newUrl = request.url().newBuilder()
.host(host)
.build();
request = request.newBuilder()
.url(newUrl)
.build();
}
try {
return chain.proceed(request);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
i get this error now:
java.lang.IllegalArgumentException: unexpected host: http://myUrl.com/ at okhttp3.HttpUrl$Builder.host(HttpUrl.java:754)
problem is set host by setHost method on this line of code:
HttpUrl newUrl = request.url().newBuilder()
.host(host)
.build();
Based on this github comment, the solution is to replace
HttpUrl newUrl = request.url().newBuilder()
.host(host)
.build();
with
HttpUrl newUrl = HttpUrl.parse(host);
You should use interceptor like this:
class HostSelectionInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
apiHost?.let { host ->
val request = chain.request()
val newUrl = request.url.newBuilder().host(host).build()
val newRequest = request.newBuilder().url(newUrl).build()
return chain.proceed(newRequest)
}
throw IOException("Unknown Server")
}
}
You just need to change at runtime the apiHost variable (var apiHost = "example.com"). Then add this interceptor to OkHttpClient builder:
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HostSelectionInterceptor())
.build()
Dagger2 realization in App
Network component's module
#Provides
#Singleton
OkHttpClient provideOkHttpClient() {
return new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Client-Id", Constants.CONFIG.APP_NAME)
.addHeader("RE-Phone-Number", phoneNumber)
.addHeader("RE-Access-Token", access_token);
Request request = builder.build();
return chain.proceed(request);
}
}).build();
}
It is called at the beginnig of app once. I need to change user credentials after their obtaining.
#Provides
#Singleton
Retrofit provideRetrofit(GsonConverterFactory gsonConverterFactory,
RxJavaCallAdapterFactory rxJavaCallAdapterFactory,
OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
}
Api component's module
#Module
public class ApiModule {
#Provides
#PerFragment
CompanyService provideCompanyService(Retrofit retrofit){
return retrofit.create(CompanyService.class);
}
}