【问题标题】:Only if run through Eclipse: POST on Spring Boot Controller fails with HTTP Error 415仅当通过 Eclipse 运行时:Spring Boot Controller 上的 POST 失败并出现 HTTP 错误 415
【发布时间】:2020-06-21 12:23:01
【问题描述】:

问题:

我们有一个可用的 Spring Boot 后端服务器(Java 11、Spring Boot 2.2.4.RELEASE)和一个 React 前端,它在 Docker 容器中完美运行,如果通过 java -jar app.jar 运行并且如果通过 IntelliJ 运行。

如果它通过 Eclipse 运行(在 Windows Server 2016 上运行),在尝试发送带有 JSON 正文的 POST 时,会遇到以下错误:

2020-03-09 15:09:52.515  WARN 218960 --- [nio-8080-exec-1] .c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class xxx.xxx.xxx.xxx.xxx.xxx.xxx]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `xxx.xxx.xxx.xxx.xxx.xxx.xxx`: Argument #0 has no property name, is not Injectable: can not use as Creator [constructor for xxx.xxx.xxx.xxx.xxx.xxx.xxx, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}]
2020-03-09 15:09:52.517  WARN 218960 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported]

无论通过前端代码设置的标头如何,都会发生此错误。所以通过设置Content-Type: application/json,同样的错误会发生(见下面的标题)。

有没有人遇到过类似的只发生在 Eclipse 中的事情?

我已经检查了编码(在 Eclipse 中将所有内容设置为 UTF-8),并确保所有环境在 IntelliJ 和 Eclipse 中都使用相同的 Java 版本(我使用的是通过 SDKMan 安装的 AdoptOpenJDK 11.0.6.hs-adpt)。

编译后的类文件看起来相同。运行应用程序时的类路径包含相同的杰克逊库(总体上看起来非常相似)。

我相信这与 Eclipse 如何运行应用程序有关。有谁知道为什么会观察到这种奇怪的行为?为什么只使用 Eclipse?而且由于我们有一些开发人员使用 Eclipse,我们该如何解决呢?


有关请求的更多信息:

通过浏览器看到的请求标头:

POST /server/configure HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 510
Accept: application/json, text/plain, */*
Sec-Fetch-Dest: empty
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Content-Type: application/json
Origin: http://localhost:3000
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,de-DE;q=0.8,de;q=0.7

然后回应:

{"timestamp":"2020-03-09T14:00:27.285+0000","status":415,"error":"Unsupported Media Type","message":"Content type 'application/json;charset=UTF-8' not supported","path":"/server/configure"}

以及响应头:

HTTP/1.1 415
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 09 Mar 2020 13:53:05 GMT
Keep-Alive: timeout=60
Connection: keep-alive

有关该应用的更多信息:

控制器如下(简化):

package xxx.xxx.xxx.xxx.xxx.xxx;

...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
...

@Controller
@RequestMapping("/server")
@CrossOrigin(origins = "http://localhost:3000")
public class Controller {

    private someService SomeService;

    @Autowired
    public Controller(SomeService someService) {
        this.someService= someService;
    }

    @PostMapping(value = "/configure", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public String configureConnection(@RequestBody ConnectionConfiguration connectionConfiguration) throws MalformedURLException {
       String serverId = someService.configureConnection(connectionConfiguration);
       return serverId;
   }

}

ConnectionConfiguration 类如下(这里显示的属性较少):

package xxx.xxx.xxx.xxx.xxx.xxx;

import com.fasterxml.jackson.annotation.JsonCreator;

public class ConnectionConfiguration {

    private String someProperty;

    @JsonCreator
    public ConnectionConfiguration(String someProperty) {
        this.someProperty= someProperty;
    }

    public String getSomeProperty() {
        return someProperty;
    }

}

关于项目结构,我们有一个根文件夹,其中包含一个 backend 和一个 frontend 文件夹。每个(根、后端和前端)都包含一个 build.gradle 文件,通过根文件夹中的 build.gradle 链接在一起。通过这样做,我们可以进行构建前端的生产构建,将静态 HTML 和 JavaScript 文件复制到后端的 resources/static 文件夹,然后构建 Spring Boot jar。然后,在生产模式下,前端通过 Spring Boot 的 Tomcat 交付。

对于开发,我们通常通过npm run start 为前端启动一个 Webpack 开发服务器,并通过 IDE 从包含 main 方法的 Application.java 类启动 Spring Boot 应用程序:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

【问题讨论】:

    标签: java eclipse spring-boot http-status-code-415


    【解决方案1】:

    在拥有相同的类路径后,我们检查了构建的 .class 文件是否真的相同 - 它们不是(见下文)。我们意识到,在 Eclipse 的 Preferences/Java/Compiler 中设置“存储有关方法参数的信息(可通过反射使用)”选项后,everythinkg 在通过 Eclipse 运行后端时也能正常工作:

    知道了这一点,反序列化为什么不起作用是有道理的。 Jackson 依赖于我们构造函数中带有 @JsonCreator 注释的参数名称来执行此操作,它通过反射获取名称。

    另见:

    https://github.com/FasterXML/jackson-modules-java8/tree/master/parameter-names

    Why when a constructor is annotated with @JsonCreator, its arguments must be annotated with @JsonProperty?


    另一种解决方案,可能更安全,因为您不必编辑 IDE 的设置:

    如果我们用@JsonProperty("name")注释参数,它也可以在没有-parameters编译选项的情况下工作:

    package xxx.xxx.xxx.xxx.xxx.xxx;
    
    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public class ConnectionConfiguration {
    
        private String someProperty;
    
        @JsonCreator
        public ConnectionConfiguration(@JsonProperty("someProperty") String someProperty) {
            this.someProperty= someProperty;
        }
    
        public String getSomeProperty() {
            return someProperty;
        }
    
    }
    

    字节码部分(没有@JsonProperty 注释):

    构建 -parameters:

    ...
      // access flags 0x1
      // signature (Ljava/lang/String;)V
      // declaration: java.lang.String)
      public <init>(Ljava/lang/String;)V
        // parameter  someProperty
      @Lcom/fasterxml/jackson/annotation/JsonCreator;()
       L0
        LINENUMBER 17 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.<init> ()V
    ...
    

    构建没有 -parameters:

    ...
      // access flags 0x1
      // signature (Ljava/lang/String;)V
      // declaration: java.lang.String)
      public <init>(Ljava/lang/String;)V
      @Lcom/fasterxml/jackson/annotation/JsonCreator;()
       L0
        LINENUMBER 16 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.<init> ()V
    ...
    

    【讨论】:

      猜你喜欢
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 2012-07-14
      • 2018-10-22
      • 1970-01-01
      相关资源
      最近更新 更多