Проблема
Мне нужно подключиться к конечной точке REST API из бэкэнда, дело в том, что для доступа к этой конечной точке мне нужно пройти через VPN. В противном случае хост недоступен. На рабочем столе все работает нормально, я открываю Postman, нажимаю конечную точку GET и получаю ответ. Однако, когда я пытаюсь подключиться к той же конечной точке через мое Android-устройство Retrofit, выдается исключение UnknownHostException.
Контекст
URL-адрес конечной точки имеет вид https://api.something.something.net/. Я использую внедрение зависимостей с помощью Dagger, поэтому у меня есть NetworkModule, который выглядит так:
Код: Выделить всё
...
NetworkModule("https://api.something.something.net/")
...
@Module
class NetworkModule(
private val baseHost: String
) {
...
@Provides
@Named("authInterceptor")
fun providesAuthInterceptor(
@Named("authToken") authToken: String
): Interceptor {
return Interceptor { chain ->
var request = chain.request()
request = request.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer $authToken")
.build()
val response = chain.proceed(request)
}
}
...
@Provides
@Singleton
fun provideOkHttpClient(
curlInterceptor: CurlInterceptor,
@Named("authInterceptor") authInterceptor: Interceptor
): OkHttpClient {
val builder = OkHttpClient.Builder()
builder.addInterceptor(authInterceptor)
builder.addInterceptor(curlInterceptor)
return builder.build()
}
...
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient, gson: Gson): Retrofit {
return Retrofit.Builder()
.baseUrl(baseHost)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
}
}
Код: Выделить всё
class MyApiRepositoryImpl(
val myRetrofitApi: MyRetrofitApi,
val uiScheduler: Scheduler,
val backgroundScheduler: Scheduler
) : MyApiRepository {
override fun getSomethingFromTheApi(): Observable> {
return myRetrofitApi.getResponseFromEndpoint()
.map {
if (it.isSuccessful) {
DataResource(it.body()?.list!!, ResourceType.NETWORK_SUCCESS)
} else {
throw RuntimeException("Network request failed code ${it.code()}")
}
}
.subscribeOn(backgroundScheduler)
.observeOn(uiScheduler)
.toObservable()
}
}
Код: Выделить всё
interface MyRetrofitApi {
@GET("/v1/something/")
fun getResponseFromEndpoint(): Single
}
Что я пробовал
- Я переключил Retrofit на Volley, а затем на Ion, просто чтобы убедитесь, что это не связано с остальным клиентом. У меня во всех случаях одно и то же исключение:
Код: Выделить всё
java.net.UnknownHostException: Unable to resolve host "api.something.something.net": No address associated with hostnameКод: Выделить всё
com.android.volley.NoConnectionError: java.net.UnknownHostException: Unable to resolve host "api.something.something.net": No address associated with hostname- < li>В OkHttpClient я попытался установить для FollowSslRedirects значения true и false. FollowRedirects на true и false. Установите hostnameVerifier, чтобы разрешить прохождение любого имени хоста. Установите SSLSocketFactory, чтобы разрешить прохождение любых неподписанных сертификатов.
- В моем манифесте я установил для параметра android:networkSecurityConfig значение:< /li>
- Я тестировал приложение на своем устройстве Android (Android Nougat), на эмуляторах Nougat, Marshmallow и Oreo, а также на эмуляторе Genymotion с Nougat.
Я попробовал подключиться к случайной общедоступной конечной точке (), и это сработало отлично. Так что проблема не в подключении к Интернету.Код: Выделить всё
https://jsonplaceholder.typicode.com/todos/1 - В моем манифесте установлены следующие три разрешения:
Код: Выделить всё
android.permission.INTERNET android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_WIFI_STATE
- На моем ноутбуке я использую Cisco AnyConnect, на моем устройстве Android я При использовании приложения Cisco AnyConnect и AFAIK на эмуляторах и Genymotion он должен использовать ту же сеть, что и хост-компьютер.
Пара странных вещей
Да, это становится еще страннее. Если я захожу в конечную точку через браузер Chrome на своем устройстве или в эмуляторе, меня перенаправляют на страницу входа в систему, это потому, что я не отправляю токен авторизации. Теперь, если я проверю ответы сети через chrome://inspect, я смогу получить IP-адрес хоста. Теперь, если я изменю базовый URL-адрес NetworkModule по IP-адресу и добавлю эту строку в перехватчик авторизации:
Код: Выделить всё
@Provides
@Named("authInterceptor")
fun providesAuthInterceptor(
@Named("authToken") authToken: String
): Interceptor {
return Interceptor { chain ->
var request = chain.request()
request = request.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer $authToken")
.build()
val response = chain.proceed(request)
response.newBuilder().code(HttpURLConnection.HTTP_SEE_OTHER).build() //
Подробнее здесь: [url]https://stackoverflow.com/questions/53292330/retrofit-request-throwing-unknownhostexception-behind-vpn[/url]
Мобильная версия