添加基本​​身份验证后,出现NonRepeatableRequestException

时间:2018-08-27 08:10:32

标签: java http apache-httpclient-4.x

我正在使用最新的Apache http:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient-osgi</artifactId>
    <version>4.5.6</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore-osgi</artifactId>
    <version>4.4.10</version>
</dependency>

我有以下操作:

public void store(InputStream input) throws IOException {
    HttpClientBuilder builder = HttpClientBuilder.create();
    if (StringUtils.isNotBlank(username)) {
      CredentialsProvider provider = new BasicCredentialsProvider();
      UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username.trim(), StringUtils.trimToEmpty(password));
      provider.setCredentials(AuthScope.ANY, credentials);
      builder.setDefaultCredentialsProvider(provider);
    }
    HttpClient client = builder.build();
    HttpPost post  = new HttpPost(uri);
    post.setEntity(new InputStreamEntity(input));
    HttpResponse response = client.execute(post);
}   

在基本身份验证处于活动状态之前,一切正常,但是,添加基本身份验证后,出现以下错误:

  

由以下原因引起:org.apache.http.client.NonRepeatableRequestException:   无法使用不可重复的请求实体重试请求。在   org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:226)     在   org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)     在   org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)     在   org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)     在   org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)     ...还有6个

我发现了以下错误报告:https://github.com/http-builder-ng/http-builder-ng/issues/10,但是它已分配给另一个问题。

是什么导致错误?如何在Apache httpclient中使用基本身份验证?我不知道什么是“可重复HTTP请求”,据我所知,所有客户端需要设置的都是Authorization标头。我是否可能在服务器上配置了某些错误,从而需要“可重复的” HTTP请求?

2 个答案:

答案 0 :(得分:0)

似乎基本的身份验证模型在apache httpclient中被破坏了。该库尝试绕过身份验证并发送不带Authorization头的请求,这当然会失败。然后,库尝试重新发送请求,但失败了,因为无法倒退InputStream

解决方案是忘记BasicCredentialsProvider并使用HttpRequestInterceptor设置标题:

HttpClientBuilder builder = HttpClientBuilder.create();
if (StringUtils.isNotBlank(username)) {
  builder.addInterceptorFirst(new HttpRequestInterceptor()
  {

    @Override
    public void process(HttpRequest request, HttpContext context) throws HttpException, IOException
    {
      String token = Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
      request.addHeader("Authorization", "Basic "+token);
    }
  });
}   

答案 1 :(得分:0)

我不同意OP自己的解决方案,因为它某种程度上非常骇人,并且绕过了图书馆的凭证机制。

HTTP实体有几种实体类型,如here所述。因此,既然您知道了,就无法在方案中使用可重复的实体,如何使用独立的实体或使用缓冲区的包装器。

您可以使用单线实现。没有尝试过,我认为正确的解决方案是:

grammar RecordGrammar;

records     : record+ EOF;
record      : Record name tracks EndRecord;
tracks      : track+;
track       : Track length EndTrack;
length      : Length Number;
name        : Name+;

Record      : 'record';
EndRecord   : 'endrecord';
Track       : 'track';
EndTrack    : 'endtrack';
Length      : 'length';
Name        : [a-zA-Z]+;
Number      : [0-9]+;
WS          : [ \t\r\n]+ -> skip;