Информация об изменениях

Сообщение Как в Spring Boot включить Content-Length в ответ? от 06.05.2022 10:01

Изменено 06.05.2022 10:54 Worminator X

Как в Spring Boot включить Content-Length в ответ?
Разрабатываю онлайн-игру с сервером на Java и клиентом на Си. Для передачи данных используется REST + XML (Jackson). Клиент подключается к серверу через TCP сокет, отправляет HTTP-запросы, парсит тело из ответа с помощью Expat'а и обновляет картинку. На сервере используется обычное Spring Boot приложение со встроенным Jetty.

Проблема в том, что по умолчанию Spring Boot использует Transfer-Encoding с разбиением ответа на chunk'и. Возвращаемые заголовки в ответе:

Content-Type: application/xml
Date: Fri, 06 May 2022 09:41:32 GMT
Transfer-Encoding: chunked

Естественно, читать такой ответ, собирая из кусков, неудобно и очень медленно. Как из заголовков убрать Transfer-Encoding: chunked и соответственно добавить Content-Length с указанием размера данных, чтобы тело шло единым набором байтов, который сразу можно было бы прочитать?

Вот пример контроллера Spring:
@RestController
@RequestMapping("/users")
public class UsersController {
    @GetMapping(value = "/")
    public List<User> getAllUsers() {
        return UsersService.getInstance().findAll();
    }

    @GetMapping(value = "/{id}")
    public ResponseEntity<User> getUser(@PathVariable String id) {
        final Optional<User> user = UsersService.getInstance().find(id);
        return user.isPresent() ?
            ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body(user.get()) :
            ResponseEntity.notFound().build();
    }
}


На StackOverflow пишут, что это Spring таким образом использует сжатие ответа. Хорошо, в файле конфигурации application.yml отключаю его:

server:
    compression:
        enabled: false
    port: 2992


Не помогает, все равно ответ возвращается с Transfer-Encoding: chunked. Как исправить?
Как в Spring Boot включить Content-Length в ответ?
Разрабатываю онлайн-игру с сервером на Java и клиентом на Си. Для передачи данных используется REST + XML (Jackson). Клиент подключается к серверу через TCP сокет, отправляет HTTP-запросы, парсит тело из ответа с помощью Expat'а и обновляет картинку. На сервере используется обычное Spring Boot приложение со встроенным Jetty.

Проблема в том, что по умолчанию Spring Boot использует Transfer-Encoding с разбиением ответа на chunk'и. Возвращаемые заголовки в ответе:

Content-Type: application/xml
Date: Fri, 06 May 2022 09:41:32 GMT
Transfer-Encoding: chunked

Естественно, читать такой ответ, собирая из кусков, неудобно и очень медленно. Как из заголовков убрать Transfer-Encoding: chunked и соответственно добавить Content-Length с указанием размера данных, чтобы тело шло единым набором байтов, который сразу можно было бы прочитать?

Вот пример контроллера Spring:
@RestController
@RequestMapping("/users")
public class UsersController {
    @GetMapping(value = "/")
    public List<User> getAllUsers() {
        return UsersService.getInstance().findAll();
    }

    @GetMapping(value = "/{id}")
    public ResponseEntity<User> getUser(@PathVariable String id) {
        final Optional<User> user = UsersService.getInstance().find(id);
        return user.isPresent() ?
            ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body(user.get()) :
            ResponseEntity.notFound().build();
    }
}


На StackOverflow пишут, что это Spring таким образом использует сжатие ответа. Хорошо, в файле конфигурации application.yml отключаю его:

server:
    compression:
        enabled: false
    port: 2992


Не помогает, все равно ответ возвращается с Transfer-Encoding: chunked. Как исправить?

UPD Работает, если задать Content-Length в контроллере вручную через метод ReponseEntity:
@RestController
@RequestMapping("/users")
public class UsersController {
    @GetMapping(value = "/")
    public List<User> getAllUsers() {
        return UsersService.getInstance().findAll();
    }

    @GetMapping(value = "/{id}")
    public ResponseEntity<User> getUser(@PathVariable String id) {
        final Optional<User> user = UsersService.getInstance().find(id);
        return user.isPresent() ?
            ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).contentLength(
                getContentLength(user.get())
            ).body(user.get()) :
            ResponseEntity.notFound().build();
    }

    private static long getContentLength(User user) {
        try {
            final XmlMapper mapper = new XmlMapper();
            final String xml = mapper.writeValueAsString(user);
            return xml.length();
        } catch (JsonProcessingException ex) {
            return 0;
        }            
    }
}

Но это костыль и быдлокод, 2 раза выполяется сериализация для объекта. Очевидно, Spring должен рассчитывать его автоматически. Как это сделать?