J'utilise Retrofit pour faire une demande de base POST, et je fournis un @Body de base pour la demande.
@POST("/rest/v1/auth/login")
LoginResponse login(@Body LoginRequest loginRequest);
Lorsque je construis l'interface pour Retrofit, je fournis mon propre OkHttpClient personnalisé, et tout ce que je fais, c'est d'ajouter ma propre authentification personnalisée:
@Provides
@Singleton
public Client providesClient() {
OkHttpClient httpClient = new OkHttpClient();
httpClient.setAuthenticator(new OkAuthenticator() {
@Override
public Credential authenticate(Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
return getCredential();
}
@Override
public Credential authenticateProxy(Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
return getCredential();
}
});
return new OkClient(httpClient);
}
Cela fonctionne très bien lorsque j'envoie des demandes directement avec OKHttp et d'autres demandes GET avec modification, mais lorsque j'utilise la modification pour effectuer une demande POST, l'erreur suivante s'affiche:
Caused by: Java.net.HttpRetryException: Cannot retry streamed HTTP body
at com.squareup.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.Java:324)
at com.squareup.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.Java:508)
at com.squareup.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.Java:136)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.Java:94)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.Java:49)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.Java:357)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.Java:282)
at $Proxy3.login(Native Method)
at com.audax.paths.job.LoginJob.onRunInBackground(LoginJob.Java:41)
at com.audax.library.job.AXJob.onRun(AXJob.Java:25)
at com.path.Android.jobqueue.BaseJob.safeRun(BaseJob.Java:108)
at com.path.Android.jobqueue.JobHolder.safeRun(JobHolder.Java:60)
at com.path.Android.jobqueue.executor.JobConsumerExecutor$JobConsumer.run(JobConsumerExecutor.Java:172)
at Java.lang.Thread.run(Thread.Java:841)
J'ai joué avec ça. Si je supprime l'authentification et que je pointe vers un serveur qui ne nécessite pas l'authentification, cela fonctionne correctement.
Pas sûr de savoir comment contourner ça. Toute aide serait merveilleuse.
Votre meilleur choix est de fournir vos informations d'identification à Retrofit via un RequestInterceptor au lieu de la variable OkAuthenticator
de OkHttp. Cette interface fonctionne mieux lorsque la demande peut être réessayée, mais dans votre cas, nous avons déjà jeté le corps de publication au moment où nous découvrons que cela est nécessaire.
Vous pouvez continuer à utiliser la classe Credential d'OkAuthenticator qui peut coder votre nom d'utilisateur et votre mot de passe au format requis. Le nom d'en-tête que vous voulez est Authorization
.
Merci Jesse.
Juste au cas où cela aiderait, voici le code que j'ai créé pour l'authentification de base.
Tout d'abord, l'init dans la classe MyApplication
:
ApiRequestInterceptor requestInterceptor = new ApiRequestInterceptor();
requestInterceptor.setUser(user); // I pass the user from my model
ApiService apiService = new RestAdapter.Builder()
.setRequestInterceptor(requestInterceptor)
.setServer(Constants.API_BASE_URL)
.setClient(new OkClient()) // The default client didn't handle well responses like 401
.build()
.create(ApiService.class);
Et puis la ApiRequestInterceptor
:
import Android.util.Base64;
import retrofit.RequestInterceptor;
/**
* Interceptor used to authorize requests.
*/
public class ApiRequestInterceptor implements RequestInterceptor {
private User user;
@Override
public void intercept(RequestFacade requestFacade) {
if (user != null) {
final String authorizationValue = encodeCredentialsForBasicAuthorization();
requestFacade.addHeader("Authorization", authorizationValue);
}
}
private String encodeCredentialsForBasicAuthorization() {
final String userAndPassword = user.getUsername() + ":" + user.getPassword();
return "Basic " + Base64.encodeToString(userAndPassword.getBytes(), Base64.NO_WRAP);
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Étendre la réponse de Naren:
Vous construisez auth String
comme ceci:
String basicAuth = "Basic " + Base64.encodeToString(String.format("%s:%s", "your_user_name", "your_password").getBytes(), Base64.NO_WRAP);
Et puis vous passez basicAuth
au service comme authorization
.
@GET("/user")
void getUser(@Header("Authorization") String authorization, Callback<User> callback)
Pour une autorisation de base, vous pouvez fournir un en-tête du type:
@GET("/user")
void getUser(@Header("Authorization") String authorization, Callback<User> callback)
Si vous faites cela avec la dernière version de Retrofit/OkHttp, les solutions actuelles ne sont pas suffisantes. Retrofit n'offre plus un RequestInterceptor, vous devez donc utiliser les intercepteurs d'OkHttp pour accomplir une tâche similaire:
Créez votre intercepteur:
public class HttpAuthInterceptor implements Interceptor {
private String httpUsername;
private String httpPassword;
public HttpAuthInterceptor(String httpUsername, String httpPassword) {
this.httpUsername = httpUsername;
this.httpPassword = httpPassword;
}
@Override public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", getAuthorizationValue())
.build();
return chain.proceed(newRequest);
}
private String getAuthorizationValue() {
final String userAndPassword = "httpUsername" + ":" + httpPassword;
return "Basic " + Base64.encodeToString(userAndPassword.getBytes(), Base64.NO_WRAP);
}
}
Vous devez ajouter l'intercepteur à votre client OkHttp:
// Create your client
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpAuthInterceptor("httpUsername", "httpPassword"))
.build();
// Build Retrofit with your client
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.build();
// Create and use your service that now authenticates each request.
YourRetrofitService service = retrofit.create(YourRetrofitService.class);
Je n'ai pas testé le code ci-dessus, il est donc possible que de légères modifications soient nécessaires. Je programme dans Kotlin pour Android maintenant-a-days.