SpringBoot中如何在控制器之前重写InputStream?

问题描述 投票:0回答:3

如何在 Spring Boot 中覆盖@RequestBody 内容到达控制器之前?

  1. 我知道有 WebMvcConfigurerHandlerInterceptorAdapter 类可以在控制器之前处理请求。

  2. 我也用谷歌搜索了RequestBodyAdviceAdapter

有几个链接不适用于 Spring Boot。

如何多次读取request.getInputStream()

如何在 Spring Boot 中到达控制器之前修改请求正文

现在我可以将输入流读入字符串,进行一些修改并设置回控制器的输入流吗?

spring-boot inputstream
3个回答
6
投票

解决方案1:(我认为更好的解决方案) 正如评论中建议的,尝试为对象使用 @JsonProperty 或自定义 De-/Serializer。

解决方案2: 添加@ControllerAdvice并实现RequestBodyAdvice并覆盖beforeBodyRead为

@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    InputStream body = inputMessage.getBody();
    String bodyStr = IOUtils.toString(body, Charset.forName("UTF-8"));
    /*
    Update bodyStr as you wish
    */
    HttpInputMessage ret = new MappingJacksonInputMessage(new ByteArrayInputStream(bodyStr.getBytes()), inputMessage.getHeaders()); //set the updated bodyStr
    return ret;
}

0
投票

这看起来是 Servlet 过滤器的一个很好的用例。过滤器会先于控制器接收请求,允许您修改请求,然后您可以将请求传递到过滤器链以供控制器处理。

示例: https://www.baeldung.com/spring-boot-add-filter

您将需要编写一个自定义的 HttpServletRequestWrapper。在这个包装类的构造函数中将接收请求。将InputStream读入变量(此时可以修改流)。还要重写 getInputStream() 和 getReader() 方法以返回修改后的流(而不是原始流)。

在过滤器中,创建包装器的新实例并将传入请求传递到构造函数中。完成修改后,将打包的请求传递到

chain.doFilter(myWrappedRequest, response)
以允许下游控制器处理它。

这是如何创建包装器并在过滤器中使用它的示例:https://howtodoinjava.com/servlets/httpservletrequestwrapper-example-read-request-body/


0
投票

我对 Lipu 的赞扬,他直接启发了我解决类似问题的方法:我有一系列域 java 记录类,这些类将通过 Postman(或其他外部系统)通过 REST 调用提交,我需要它们全部分配一个时间戳和一个UUID,但我不想在域记录的构造函数中编写任何代码,因为外部 REST 调用会经过 Jackson 反序列化,并且无论我包含多少个更简单的构造函数,都会调用记录规范构造函数。

所以使用 Spring @ControllerAdvice 我实现了:

@ControllerAdvice
@AllArgsConstructor
public class StorableRecordAdvice extends RequestBodyAdviceAdapter {
    final private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    /**
     * Add the following fields to any request body:
     * <li>timestamp</li>
     * <li>uuid</li>
     */
    @Override
    public HttpInputMessage beforeBodyRead(
            HttpInputMessage inputMessage,
            MethodParameter parameter,
            Type targetType,
            Class<? extends HttpMessageConverter<?>> converterType
    ) throws IOException {
        InputStream body = inputMessage.getBody();
        JsonNode jsonNode = objectMapper.readTree(IOUtils.toString(body, StandardCharsets.UTF_8));
        ((ObjectNode) jsonNode).put("timestamp", LocalDateTime.now(ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC));
        ((ObjectNode) jsonNode).put("uuid", UUID.randomUUID().toString());
        return new MappingJacksonInputMessage(new ByteArrayInputStream(jsonNode.toString().getBytes()), inputMessage.getHeaders());
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.