【问题标题】:Spring rest template 401 error responseSpring Rest模板401错误响应
【发布时间】:2016-12-22 11:10:23
【问题描述】:

我有一个休息控制器在 http://localhost:8080/documents 上回答。 我应该有一个授权标头来调用它。

所以在我的客户端代码中我有:

HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, "myToken");
HttpEntity entity = new HttpEntity(null, headers);

restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

response = restTemplate.exchange("http://localhost:8080/documents", HttpMethod.GET, entity, Document[].class);

一切正常。 之后我想测试错误。 所以,我删除了授权标头。

当我使用 postman 之类的工具进行测试时,我收到 401 响应。 但是使用我的 rest 模板,我只收到 IllegalArgumentException。

我还测试了 ResponseErrorHandler。

public class MyErrorHandler implements ResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
        return false; //i've also tried return true
    }

    @Override
    public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
        String theString = IOUtils.toString(clientHttpResponse.getBody());
        FunctionalTestException exception = new FunctionalTestException();
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("code", clientHttpResponse.getStatusCode().toString());
        properties.put("body", theString);
        properties.put("header", clientHttpResponse.getHeaders());
        exception.setProperties(properties);
        throw exception;
    }
}

在我的客户中我有

restTemplate.setErrorHandler(new MyErrorHandler());

没用。

所以我的问题是如何使用其余模板找到我的 401 错误响应。

这是一个例外:

java.lang.IllegalArgumentException: invalid start or end

和堆栈跟踪:

sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1455)
sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
sun.net.www.protocol.http.HttpURLConnection.getHeaderField(HttpURLConnection.java:2979)
java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:489)
org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:84)
org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:619)
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580)
org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:498)
org.boite.dq.steps.UnauthorizedUser.callListCategories(UnauthorizedUser.java:61)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.jbehave.core.steps.StepCreator$ParametrisedStep.perform(StepCreator.java:733)
org.jbehave.core.embedder.PerformableTree$FineSoFar.run(PerformableTree.java:346)
org.jbehave.core.embedder.PerformableTree$PerformableSteps.perform(PerformableTree.java:1088)
org.jbehave.core.embedder.PerformableTree$AbstractPerformableScenario.performRestartableSteps(PerformableTree.java:953)
org.jbehave.core.embedder.PerformableTree$NormalPerformableScenario.perform(PerformableTree.java:992)
org.jbehave.core.embedder.PerformableTree$PerformableScenario.perform(PerformableTree.java:902)
org.jbehave.core.embedder.PerformableTree$PerformableStory.performScenarios(PerformableTree.java:825)
org.jbehave.core.embedder.PerformableTree$PerformableStory.perform(PerformableTree.java:798)
org.jbehave.core.embedder.PerformableTree.performCancellable(PerformableTree.java:422)
org.jbehave.core.embedder.PerformableTree.perform(PerformableTree.java:393)
org.jbehave.core.embedder.StoryManager$EnqueuedStory.call(StoryManager.java:292)
org.jbehave.core.embedder.StoryManager$EnqueuedStory.call(StoryManager.java:266)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)

【问题讨论】:

  • 请发布您在客户端获得的完整堆栈跟踪。您是否也检查过您的服务器代码是否到达授权点?
  • 感谢@jannis,我已经添加了堆栈跟踪,是的,我确定我的代码到达了授权点。
  • 我无法重现这个。在我的情况下,这正常工作 - 我得到一个 401 状态的响应。请发布您使用 Postman 获得的整个 401 HTTP 响应。也许它的格式不正确,这会导致 RestTemplate 崩溃。
  • 看看this。看起来你遇到的问题很准确。由此判断,您的响应标头可能会导致崩溃。

标签: spring rest resttemplate


【解决方案1】:

崩溃发生在HttpURLConnection::getHeaderField,所以我怀疑您的响应标头之一格式错误(不是HttpURLConnection 期望的那样)。通常401 响应带有WWW-Authenticate 响应标头,将代理指向服务支持的身份验证方法。我怀疑这个标题会导致崩溃。

A bug report 在 Jersey 的问题跟踪器中显示 HttpURLConnectionWWW-Authentication 标头格式设置了一些限制。在这种特殊情况下,导致类似崩溃的值是oauth_problem=token_rejected。建议的解决方法是:

解决方法是发送有效的标头值(符合规范)或使用 ApacheConnector

【讨论】:

  • “解决方法”发送有效值...:D
  • 嗯,这肯定是问题所在,由于春天和一些商业案例,我无法纠正它,但这是一种很好的搜索方式,谢谢
  • 我现在被这个问题困扰了,我可以确认HttpURLConnection 期望WWW-Authenticate 的值符合 RFC 2617,这意味着它期望一个方案,例如“基本”或“摘要”后跟一个空格,然后是 "realm=..." 或其他值。标头解析器因标头值中没有空格这一事实而窒息。
猜你喜欢
  • 1970-01-01
  • 2017-10-20
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2017-08-19
  • 2018-09-06
相关资源
最近更新 更多