Spring MVC-контроллер с несколькими @RequestBody

Мне было интересно, может ли, например, контроллер SpringMVC иметь подпись метода, например

@RequestMapping(value = "/target", method = RequestMethod.POST)
@ResponseBody
public void acceptObject(@RequestBody MyObjectDto dto,@RequestBody String messageBody) {
    //Authenticate messageBody
    //Process mapped DTO
}

Предполагалось, что JSON будет отправлен на этот контроллер, тело необработанного сообщения будет аутентифицировано для обеспечения целостности, и если это будет правильно, JSON будет сопоставлен с DTO, который может быть передан для обработки.

В настоящий момент я заканчиваю

java.io.IOException: Stream closed

Ответ 1

Spring использует интерфейс HandlerMethodArgumentResolver, чтобы определить, какие аргументы он передаст вашим методам обработчика. Для параметров, аннотированных с помощью @RequestBody, он использует класс RequestResponseBodyMethodProcessor. Этот класс в основном выглядит в виде набора объектов HttpMessageConverter для одного, который может читать content-type запроса и может преобразовать его в указанный тип. Если он находит один, он передает тело HttpServletRequest как InputStream в объект HttpMessageConverter.

В этом случае вы, вероятно, найдете работу десериализатора JSON. Это очень вероятно (видя IOException, который вы получаете), потребляя поток, а затем закрывая его.

Таким образом, действительно, этот способ делать вещи напрямую невозможен.

Одним из решений является создание Filter, которое обертывает HttpServletRequest в вашей собственной реализации, которая буферизирует InputStream, чтобы сделать ее повторно используемой/повторно читаемой столько раз, сколько требуется. Но опять же правила десериализации из тела могут быть приняты Spring, а не то, что вы хотите точно. В этом случае вы можете создать свои собственные Annotation и HandlerMethodArgumentResolver, которые затем регистрируются приложением в вашей конфигурации. Затем вы можете точно контролировать, как вещи десериализуются из тела запроса.

Другим решением является объединение как MyObjectDto, так и messageBody в один DTO, если это имеет смысл для вашей модели данных (и для процесса десериализации Spring).