改造带有基本 HTTP 身份验证的开机自检请求:"Cannot retry streamed HTTP body"



我正在使用Raturofit来执行基本帖子请求,并且我为请求提供了基本 @body。

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

当我构建改造的界面时,我会提供自己的自定义OKHTTPCLCLIENT,而我要做的就是添加自己的自定义身份验证:

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

当我直接向OKHTTP发送请求时,这很棒,而其他Raterrofit的获取请求进行了,但是当我使用Raterofit进行邮政请求时,我会收到以下错误:

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)

我已经玩过它。如果我删除身份验证,并指向不需要身份验证的服务器,则可以正常工作。

  1. 所以我必须发送信息。
  2. 获得身份验证挑战请求。
  3. 回应挑战请求。
  4. 试图再次重新发出请求,然后被抛出。

不确定如何解决这个问题。任何帮助都会很棒。

谢谢,杰西。

以防万一它有所帮助,这是我为基本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;
    }
}

扩展了纳伦的答案:

您像这样构建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<User> callback)

您最好的选择是通过RequestInterceptor而不是OKHTTTP的OkAuthenticator提供您的凭据进行改造。当请求可以重述时,该界面效果最好,但是在您的情况下,我们已经在发现必要的时候已经抛出了帖子主体。

您可以继续使用Okauthenticator的凭据类,该类可以以所需的格式编码您的用户名和密码。您想要的标题名称是Authorization

用于基本授权您可以提供一个标题:

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

如果您使用最新版本的retrofit/okhttp进行此操作,则当前的解决方案集不够。Retrofit不再提供请求Interceptor,因此您需要使用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进行Android编程。

最新更新