使用基本HTTP身份validation更改POST请求:“无法重试流式HTTP正文”

我正在使用Retrofit来做一个基本的POST请求,我正在为请求提供一个基本的@Body。

@POST("/rest/v1/auth/login") LoginResponse login(@Body LoginRequest loginRequest); 

当我为Retrofit构建界面时,我正在提供我自己的自定义OkHttpClient,而我正在做的就是添加我自己的自定义身份validation:

  @Provides @Singleton public Client providesClient() { OkHttpClient httpClient = new OkHttpClient(); httpClient.setAuthenticator(new OkAuthenticator() { @Override public Credential authenticate(Proxy proxy, URL url, List challenges) throws IOException { return getCredential(); } @Override public Credential authenticateProxy(Proxy proxy, URL url, List challenges) throws IOException { return getCredential(); } }); return new OkClient(httpClient); } 

当我使用OKHttp直接发送请求时,这很有效,而其他GET请求有改进,但是当我使用改造来执行POST请求时,我收到以下错误:

 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) 

我玩过它。 如果我删除身份validation,并指向不需要身份validation的服务器,那么它可以正常工作。

  1. 所以我必须发送信息。
  2. 获取身份validation质询请求。
  3. 响应挑战请求。
  4. 尝试再次重新发送请求,然后抛出错误。

不知道怎么解决这个问题。 任何帮助都会很精彩。

Related of "使用基本HTTP身份validation更改POST请求:“无法重试流式HTTP正文”"

您最好的办法是通过RequestInterceptor而不是OkHttp的OkAuthenticator将您的凭据提供给Retrofit。 当请求可以重试时,该接口效果最好,但在您的情况下,我们已经在我们发现必要时已经抛出了post正文。

您可以继续使用OkAuthenticator的Credential类,它可以按要求的格式对您的用户名和密码进行编码。 您想要的标题名称是Authorization

谢谢,杰西。

万一它有帮助,这是我为Basic auth做的代码。

首先, MyApplication类中的init:

 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); 

然后是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; } } 

延伸Naren的答案:

你像这样构建auth String

 String basicAuth = "Basic " + Base64.encodeToString(String.format("%s:%s", "your_user_name", "your_password").getBytes(), Base64.NO_WRAP); 

然后将basicAuth传递给服务作为authorization

 @GET("/user") void getUser(@Header("Authorization") String authorization, Callback callback) 

对于基本授权,您可以提供如下标题:

 @GET("/user") void getUser(@Header("Authorization") String authorization, Callback callback) 

如果您使用最新版本的Retrofit / OkHttp执行此操作,则当前的解决方案集是不够的。 Retrofit不再提供RequestInterceptor,因此您需要使用OkHttp的拦截器来完成类似的任务:

创建你的拦截器:

 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); } } 

您需要将拦截器添加到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); 

我没有测试上面的代码,因此可能需要进行一些细微的修改。 我现在在Kotlin for Android中编程了一天。