使用RestTemplate检索具有9xx状态代码的资源时,为什么关闭流?

时间:2017-12-21 13:38:53

标签: java spring-boot resttemplate

我的Spring Boot应用程序(版本1.5.9.RELEASE)中有一个REST端点,带有以下签名:

@RequestMapping(value = "/users", method = RequestMethod.POST) throws UsernameExistsException, EmailExistsException
public UserProxy addUser(@RequestBody UserProxy userProxy) {
  ...
}

异常由@ExceptionHandler带注释的类中的@ControllerAdvice带注释的方法处理:

@EnableWebMvc
@ControllerAdvice
public class ControllerExceptionHandler {

  @ExceptionHandler(EmailExistsException.class)
  public ResponseEntity<String> handleEmailExistsException() {
    return ResponseEntity
            .status(UsersStatusCodes.EMAIL_EXISTS) // status code 912
            .body("Email already exists");
  }

  @ExceptionHandler(UsernameExistsException.class)
  public ResponseEntity<String> handleUsernameExistsException() {
    return ResponseEntity
            .status(UsersStatusCodes.USERNAME_EXISTS) // status code 911
            .body("Username already exists");
  }
}

但是,当访问端点并抛出异常时,我在客户端检索以下异常:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://192.168.99.100:8080/users": stream is closed; nested exception is java.io.IOException: stream is closed
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:673)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:620)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:387)
at com.cheetahrunner.users.mongo.client.UsersClient.createUser(UsersClient.java:44)
at com.cheetahrunner.users.UsersServiceTest.addUser(UsersServiceTest.java:102)
at com.cheetahrunner.users.UsersServiceTest.testAddRemoveUser(UsersServiceTest.java:47)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
15:37:44.517 [main] DEBUG org.springframework.web.client.RestTemplate - POST request for "http://192.168.99.100:8080/users" resulted in 911 (null)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
Caused by: java.io.IOException: stream is closed
    at java.base/sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.ensureOpen(HttpURLConnection.java:3417)
    at java.base/sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3422)
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:83)
    at java.base/java.io.PushbackInputStream.read(PushbackInputStream.java:136)
    at org.springframework.web.client.MessageBodyClientHttpResponseWrapper.hasEmptyMessageBody(MessageBodyClientHttpResponseWrapper.java:102)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:82)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:662)
    ... 30 more

用于访问端点的代码如下:

rest.exchange(host + "/users", HttpMethod.POST, new HttpEntity<UserProxy>(user), UserProxy.class)

我不知道是什么原因导致了这个问题,是否有人对我提示?

可以通过Postman访问端点,其中收到状态文本“用户名已存在”的911响应。

2 个答案:

答案 0 :(得分:0)

实施org.springframework.web.client.ResponseErrorHandler并将其设置为您的其余模板:

rest.setErrorHandler(errorHandler);

ResponseErrorHandler有两种方法:

/**
     * Indicate whether the given response has any errors.
     * <p>Implementations will typically inspect the
     * {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response.
     * @param response the response to inspect
     * @return {@code true} if the response has an error; {@code false} otherwise
     * @throws IOException in case of I/O errors
     */
    boolean hasError(ClientHttpResponse response) throws IOException;

    /**
     * Handle the error in the given response.
     * <p>This method is only called when {@link #hasError(ClientHttpResponse)}
     * has returned {@code true}.
     * @param response the response with the error
     * @throws IOException in case of I/O errors
     */
    void handleError(ClientHttpResponse response) throws IOException;

在第二个中你应该实现错误处理逻辑。

第一个应该是:

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
    HttpStatus code = response.getStatusCode();
    return code != HttpStatus.OK && code != HttpStatus.NO_CONTENT && code != HttpStatus.NOT_MODIFIED;
}

答案 1 :(得分:0)

我遇到了同样的问题(使用相同版本的Spring Boot)并修复它我删除了内容并将内容长度标头设置为0:

df2
  .where($"type_from" === $"type_to" && $"from" =!= $"to")
  .groupBy($"to" as "nodeId", $"type_to" as "type")
  .agg(count("*") as "numLinks")
  .na.fill(0)
  .show()

val df_result = df.join(df2, Seq("nodeId", "type"), "rightouter")

不可否认,如果您确实需要发送内容,这没有多大帮助!