spring 内容类型为application/x-www-form-urlencoded的@RequestParam在Sping Boot 2.2中不工作

zqdjd7g9  于 2023-06-04  发布在  Spring
关注(0)|答案(3)|浏览(502)

从Sping Boot 2.1.10迁移到2.2.4之后,下面的方法开始为params参数返回null。这不是Spring中的一个bug,因为当我制作一个小的示例项目时它就可以工作了。它也适用于没有Content-Type的普通GET和POST:application/x-www-form-urlencoded.

@PostMapping(path = "/test", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void test(@RequestParam Map<String, String> params) {
    System.out.println(params);
}

我触发下面的请求,它在一个项目中有效,但在另一个项目中无效。我试过禁用所有过滤器和参数解析器,但都不起作用。

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "param1=1&param2=2" http://localhost:8080/test

任何帮助或想法将不胜感激。另外,如果有人能告诉我Spring解析参数的位置,我可以尝试调试并看看会发生什么。

i5desfxk

i5desfxk1#

添加另一个答案,因为我遇到了同样的问题,但解决它的方式不同。
我也有一个请求日志过滤器,它 Package 了传入的请求,并以JonasPedersen在回答中描述的类似方式缓存了响应输入流。Sping Boot 从2.1.2.RELEASE更新到2.3.4.RELEASE
我将传入的请求 Package 在一个缓存请求 Package 器中,该 Package 器缓存输入流。对我来说,问题是由于某些原因(目前还不知道)request.getParameterValue(String key)方法返回null,即使 Package 的请求显然有一个非空的参数Map。简单地访问 Package 的请求参数Map就解决了我的问题……非常奇怪。
原始的 Package 器类,与Sping Boot 2.1.2.RELEASE一起使用:

public class CachingRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] cachedBody;

    public CachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedInputStream(this.cachedBody);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        ByteArrayInputStream byteArrayInputStream =
                new ByteArrayInputStream(this.cachedBody);
        String encoding = StringUtils.isEmpty(this.getCharacterEncoding())
                ? StandardCharsets.UTF_8.name()
                : this.getCharacterEncoding();
        return new BufferedReader(new InputStreamReader(byteArrayInputStream, encoding));
    }
}

为简洁起见,省略了CachedInputStream类的实现。
简单地访问 Package 的请求Map似乎可以解决整个问题。对于Sping Boot 2.3.4.RELEASE,此版本有效(我删除了一些细节):

public class CachingRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] cachedBody;
    private final Map<String, String[]> parameterMap;

    public CachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        parameterMap = request.getParameterMap(); // <-- This was the crucial part
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return this.parameterMap; // this was added just to satisfy spotbugs
    }
}

我没有费心去深入研究这个问题,所以我不能说两个Sping Boot 版本之间发生了什么变化,或者在wrappers构造函数中访问参数Map解决了什么问题。

h4cxqtbf

h4cxqtbf2#

经过无数个小时的调试,原来是一个日志过滤器的问题。我是这样阅读请求的:

private static final class BufferedRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] buffer;

    BufferedRequestWrapper(HttpServletRequest req) throws IOException {
        super(req);

        // Read InputStream and store its content in a buffer.
        InputStream is = req.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int read;
        while ((read = is.read(buf)) > 0) {
            baos.write(buf, 0, read);
        }
        this.buffer = baos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() {
        return new BufferedServletInputStream(new ByteArrayInputStream(this.buffer));
    }

    String getRequestBody() throws IOException {
        return IOUtils.readLines(this.getInputStream(), StandardCharsets.UTF_8).stream()
                .map(String::trim)
                .collect(Collectors.joining());
    }
}

这消耗了来自内容类型application/x-www-form-urlencoded的查询参数。我的解决方案是不阅读application/x-www-form-urlencoded的输入流。

if (req.getContentType() == null || (req.getContentType() != null && !req.getContentType().startsWith("application/x-www-form-urlencoded"))) {
    // Read InputStream and store its content in a buffer.
    InputStream is = req.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int read;
    while ((read = is.read(buf)) > 0) {
        baos.write(buf, 0, read);
    }
    this.buffer = baos.toByteArray();
} else {
    buffer = new byte[0];
}
juud5qan

juud5qan3#

如果您也使用日志,则可能发生这种情况。参见https://github.com/zalando/logbook/issues/1077
显然,禁用表单日志记录可以修复它。

logbook:
  filter:
    form-request-mode: off

相关问题