Кинжал + Дооснащение. Добавление заголовков auth во время выполнения
Мне интересно, есть ли способ, чтобы Кинжал знал, что он должен воссоздать объект, когда доступны новые данные.
Экземпляр, о котором я говорю, связан с заголовками запросов, которые у меня есть для доработки. В какой-то момент (когда пользователь входит в систему) я получаю токен, который мне нужно добавить в заголовки модификации, чтобы выполнить аутентифицированные запросы. Проблема в том, что я остался с той же версией, что и без проверки подлинности. Здесь мой код для инъекций:
@Provides
@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.cache(cache).build();
client
.newBuilder()
.addInterceptor(
chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Accept", "Application/JSON");
Request request = requestBuilder.build();
return chain.proceed(request);
}).build();
return client;
}
@Provides
@Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
return retrofit;
}
@Provides
@Singleton
public NetworkService providesNetworkService(Retrofit retrofit) {
return retrofit.create(NetworkService.class);
}
Любые идеи о том, как сделать эту работу?
Ответы
Ответ 1
Пожалуйста, рассмотрите подход, указанный @oldergod, поскольку он является "официальным" и гораздо лучшим способом, тогда как подходы, упомянутые ниже не, они могут рассматриваться как обходные пути.
У вас есть несколько вариантов.
- Как только вы получите токен, вы должны исключить компонент, предоставивший экземпляр
Retrofit
, создать новый компонент и запросить новый экземпляр Retrofit
, который будет создан с помощью okhttp
экземпляр.
- Быстрая и плохая. Сохраните токен в
SharedPreferences
, создайте заголовок okhttp
, который будет применять чтение токена из SharedPreferences
. Если их нет, не отправляйте заголовок маркера.
- Даже более уродливое решение - объявить поле
static volatile String
и сделать то же самое, что и на шаге 2.
Почему второй вариант плохой? Потому что по каждому запросу вы будете проводить опрос SD-карты и получать данные оттуда.
Ответ 2
Я лично создал okhttp3.Interceptor
, который делает это для меня, который я обновляю, когда у меня есть требуемый токен. Это выглядит примерно так:
@Singleton
public class MyServiceInterceptor implements Interceptor {
private String sessionToken;
@Inject public MyServiceInterceptor() {
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder requestBuilder = request.newBuilder();
if (request.header(NO_AUTH_HEADER_KEY) == null) {
// needs credentials
if (sessionToken == null) {
throw new RuntimeException("Session token should be defined for auth apis");
} else {
requestBuilder.addHeader("Cookie", sessionToken);
}
}
return chain.proceed(requestBuilder.build());
}
}
В соответствующем компоненте кинжала я выставляю этот перехватчик, поэтому я могу установить sessionToken
, когда мне нужно.
Вот некоторые вещи, которые Джейк говорил об этом своим разговором "Модернизация доработки" для вас.
Ответ 3
На основе решения @oldergod kotlin версии с различными классами и структурой
Сделайте экземпляр Retrofit таким
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://yoururl"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(ServiceInterceptor())
//.readTimeout(45,TimeUnit.SECONDS)
//.writeTimeout(45,TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
Добавьте класс ServiceInterceptor
как ServiceInterceptor
ниже
class ServiceInterceptor : Interceptor{
var token : String = "";
fun Token(token: String ) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if(request.header("No-Authentication")==null){
//val token = getTokenFromSharedPreference();
//or use Token Function
if(!token.isNullOrEmpty())
{
val finalToken = "Bearer "+token
request = request.newBuilder()
.addHeader("Authorization",finalToken)
.build()
}
}
return chain.proceed(request)
}
}
Интерфейс входа и реализация класса данных
interface Login {
@POST("Login")
@Headers("No-Authentication: true")
fun login(@Body value: LoginModel): Call<LoginResponseModel>
@POST("refreshToken")
fun refreshToken(refreshToken: String):
Call<APIResponse<LoginResponseModel>>
}
data class LoginModel(val Email:String,val Password:String)
data class LoginResponseModel (val token:String,val
refreshToken:String)
называть это в любой деятельности, как это
val service = RetrofitClientInstance.retrofitInstance?.create(Login::class.java)
val refreshToken = "yourRefreshToken"
val call = service?.refreshToken(refreshToken)
call?.enqueue(object: Callback<LoginResponseModel>{
override fun onFailure(call: Call<LoginResponseModel>, t: Throwable) {
print("throw Message"+t.message)
Toast.makeText(applicationContext,"Error reading JSON",Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<LoginResponseModel>, response: Response<LoginResponseModel>) {
val body = response?.body()
if(body!=null){
//do your work
}
}
})