Только при запуске через Eclipse: POST на Spring Boot Controller завершается с ошибкой HTTP 415 - PullRequest
2 голосов
/ 09 марта 2020

Проблема:

У нас есть рабочий внутренний сервер Spring Boot (Java 11, Spring Boot 2.2.4.RELEASE) с интерфейсом React, который отлично работает в Docker контейнер, если проходит через java -jar app.jar и если проходит через IntelliJ .

Если он запускается через Eclipse (работает на Windows Server 2016), однако, при попытке отправить POST с телом JSON, возникает следующая ошибка:

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) и убедился, что во всех средах используется одна и та же версия Java (я использую AdoptOpenJDK 11.0.6.hs-adpt, установленный через SDKMan) в IntelliJ и Eclipse.

Файлы скомпилированных классов выглядят одинаково. Путь к классам при запуске приложения содержит те же библиотеки Джексона (и в целом выглядит очень похоже).

Я думаю, что это как-то связано с тем, как 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;
    }

}

Что касается структуры проекта, мы имеем Папка root, которая содержит backend и папку frontend . Каждый (root, backend и frontend) содержит файл build.gradle, связанный вместе через build.gradle в папке root. Сделав это, мы можем создать производственную сборку, которая создает внешний интерфейс, копирует файлы stati c HTML и JavaScript в папку resources / stati c бэкэнда, а затем создает JAR-файл Spring Boot. Затем в производственном режиме внешний интерфейс доставляется через Tomcat Spring Boot.

Для разработки мы обычно запускаем сервер разработки Webpack для внешнего интерфейса через npm run start и приложение Spring Boot из приложения. java класс, который содержит метод main через IDE:

@SpringBootApplication
public class Application {

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

}

1 Ответ

2 голосов
/ 10 марта 2020

Получив равные пути к классам, мы проверили, действительно ли встроенные файлы .class были одинаковыми - они не были ( см. Ниже ). Мы поняли, что после установки опции «Хранить информацию о параметрах метода (которую можно использовать для отражения)» в Eclipse Preferences / Java / Compiler, все работает нормально и при запуске бэкэнда, хотя Eclipse:

enter image description here

Зная это, имеет смысл, почему десериализация не работала. Для этого Джексон использует имена параметров в нашем конструкторе, помеченные @JsonCreator, и получает их путем отражения.

См. Также:

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

Почему, когда конструктор аннотируется @JsonCreator, его аргументы должны аннотироваться @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
...
...